@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
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
71
|
<div class="address-card-actions">
|
|
72
|
-
<button class="btn btn-outline btn-sm edit-address" data-address-id="{{ address.id }}">Edit</button>
|
|
73
|
-
<button class="btn btn-outline-danger btn-sm delete-address" data-address-id="{{ address.id }}">
|
|
72
|
+
<button type="button" class="btn btn-outline btn-sm edit-address" data-address-id="{{ address.id }}">Edit</button>
|
|
73
|
+
<button type="button" class="btn btn-outline-danger btn-sm delete-address" data-address-id="{{ address.id }}">
|
|
74
74
|
<span class="btn-text">Delete</span>
|
|
75
75
|
<span class="btn-loading" style="display: none;">
|
|
76
76
|
<span class="btn-spinner"></span>
|
|
@@ -97,31 +97,41 @@
|
|
|
97
97
|
</section>
|
|
98
98
|
|
|
99
99
|
<style>
|
|
100
|
+
/* Use theme-wide variables from assets/theme.css as the source of truth.
|
|
101
|
+
Map any template-specific variable names to the global theme variables
|
|
102
|
+
so we avoid hardcoded values here. */
|
|
100
103
|
:root {
|
|
101
|
-
--color-primary: #
|
|
102
|
-
--color-primary-hover: #
|
|
103
|
-
--color-primary-light: #
|
|
104
|
-
|
|
105
|
-
--color-success
|
|
106
|
-
--color-
|
|
107
|
-
|
|
108
|
-
--color-danger
|
|
109
|
-
--color-
|
|
110
|
-
--color-
|
|
111
|
-
|
|
112
|
-
--color-
|
|
113
|
-
--color-
|
|
114
|
-
|
|
115
|
-
--
|
|
116
|
-
--
|
|
117
|
-
--
|
|
118
|
-
--
|
|
119
|
-
|
|
120
|
-
--
|
|
121
|
-
--
|
|
122
|
-
--
|
|
123
|
-
|
|
124
|
-
--
|
|
104
|
+
--color-primary: {{ settings.color_primary | default: '#000000' }};
|
|
105
|
+
--color-primary-hover: {{ settings.color_primary_dark | default: '#333333' }};
|
|
106
|
+
--color-primary-light: {{ settings.color_primary_light | default: '#666666' }};
|
|
107
|
+
|
|
108
|
+
--color-success: {{ settings.color_success | default: '#22c55e' }};
|
|
109
|
+
--color-success-light: {{ settings.color_success | default: '#22c55e' }};
|
|
110
|
+
|
|
111
|
+
--color-danger: {{ settings.color_error | default: '#ef4444' }};
|
|
112
|
+
--color-danger-hover: {{ settings.color_error | default: '#ef4444' }};
|
|
113
|
+
--color-danger-light: {{ settings.color_error | default: '#ef4444' }};
|
|
114
|
+
|
|
115
|
+
--color-text: {{ settings.color_text | default: '#000000' }};
|
|
116
|
+
--color-text-light: {{ settings.color_text_muted | default: '#666666' }};
|
|
117
|
+
|
|
118
|
+
--color-background: {{ settings.color_background | default: '#ffffff' }};
|
|
119
|
+
--color-card-bg: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
120
|
+
--color-border: {{ settings.color_border | default: '#cccccc' }};
|
|
121
|
+
--color-border-light: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
122
|
+
|
|
123
|
+
--shadow-sm: var(--shadow-sm);
|
|
124
|
+
--shadow-md: var(--shadow-md);
|
|
125
|
+
--shadow-lg: var(--shadow-lg);
|
|
126
|
+
|
|
127
|
+
--radius-md: var(--radius-md);
|
|
128
|
+
--radius-lg: var(--radius-lg);
|
|
129
|
+
|
|
130
|
+
--spacing-xs: var(--space-3);
|
|
131
|
+
--spacing-sm: var(--space-4);
|
|
132
|
+
--spacing-md: var(--space-6);
|
|
133
|
+
--spacing-lg: var(--space-8);
|
|
134
|
+
--spacing-xl: var(--space-12);
|
|
125
135
|
}
|
|
126
136
|
/* Page */
|
|
127
137
|
.account-page {
|
|
@@ -159,10 +169,10 @@
|
|
|
159
169
|
|
|
160
170
|
.account-sidebar,
|
|
161
171
|
.account-content {
|
|
162
|
-
background:
|
|
172
|
+
background: var(--color-white);
|
|
163
173
|
border-radius: 14px;
|
|
164
174
|
padding: 1.75rem;
|
|
165
|
-
border: 1px solid
|
|
175
|
+
border: 1px solid var(--color-border);
|
|
166
176
|
}
|
|
167
177
|
|
|
168
178
|
/* Section header */
|
|
@@ -227,10 +237,13 @@
|
|
|
227
237
|
.default-badge {
|
|
228
238
|
background: #dcfce7;
|
|
229
239
|
color: #166534;
|
|
230
|
-
font-size:
|
|
240
|
+
font-size: 1rem;
|
|
231
241
|
padding: .3rem .6rem;
|
|
232
|
-
border-radius:
|
|
242
|
+
border-radius: 10px;
|
|
233
243
|
font-weight: 600;
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: center;
|
|
246
|
+
height: fit-content;
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
/* Body */
|
|
@@ -344,7 +357,7 @@
|
|
|
344
357
|
width: 90%;
|
|
345
358
|
max-height: 90vh;
|
|
346
359
|
overflow-y: auto;
|
|
347
|
-
|
|
360
|
+
|
|
348
361
|
}
|
|
349
362
|
|
|
350
363
|
.address-modal-header {
|
|
@@ -412,12 +425,16 @@
|
|
|
412
425
|
}
|
|
413
426
|
|
|
414
427
|
.iti__flag-container {
|
|
415
|
-
z-index:
|
|
428
|
+
z-index: 2;
|
|
429
|
+
position: relative;
|
|
430
|
+
pointer-events: auto;
|
|
416
431
|
}
|
|
417
432
|
|
|
418
433
|
.iti__selected-flag {
|
|
419
434
|
padding: 0 0.75rem 0 0.5rem;
|
|
420
435
|
border-right: 1px solid #d1d5db;
|
|
436
|
+
cursor: pointer;
|
|
437
|
+
pointer-events: auto;
|
|
421
438
|
}
|
|
422
439
|
|
|
423
440
|
.iti__selected-flag:hover {
|
|
@@ -465,6 +482,7 @@
|
|
|
465
482
|
width: 18px;
|
|
466
483
|
height: 18px;
|
|
467
484
|
cursor: pointer;
|
|
485
|
+
-webkit-appearance: auto !important;
|
|
468
486
|
}
|
|
469
487
|
|
|
470
488
|
.form-actions {
|
|
@@ -486,7 +504,6 @@
|
|
|
486
504
|
align-items: center;
|
|
487
505
|
gap: 0.5rem;
|
|
488
506
|
}
|
|
489
|
-
|
|
490
507
|
.btn-spinner {
|
|
491
508
|
width: 14px;
|
|
492
509
|
height: 14px;
|
|
@@ -526,7 +543,7 @@
|
|
|
526
543
|
<div class="address-modal-overlay" data-address-modal-close></div>
|
|
527
544
|
<div class="address-modal-content">
|
|
528
545
|
<div class="address-modal-header">
|
|
529
|
-
<
|
|
546
|
+
<h5 id="address-modal-title">Add New Address</h5>
|
|
530
547
|
<button class="address-modal-close" data-address-modal-close aria-label="Close">×</button>
|
|
531
548
|
</div>
|
|
532
549
|
<form class="address-form" id="address-form" action="/webstoreapi/addresses" method="post">
|
|
@@ -591,7 +608,7 @@
|
|
|
591
608
|
|
|
592
609
|
<div class="form-group">
|
|
593
610
|
<label for="address-phone" class="form-label">Phone</label>
|
|
594
|
-
<input type="tel" id="address-phone" name="phone" class="form-input">
|
|
611
|
+
<input type="tel" id="address-phone" pattern="[\d\s()+-]{7,20}" name="phone" class="form-input">
|
|
595
612
|
</div>
|
|
596
613
|
|
|
597
614
|
<div class="form-group">
|
|
@@ -643,7 +660,7 @@
|
|
|
643
660
|
}
|
|
644
661
|
}
|
|
645
662
|
|
|
646
|
-
document.addEventListener('DOMContentLoaded',
|
|
663
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
647
664
|
const modal = document.getElementById('address-modal');
|
|
648
665
|
const openButtons = document.querySelectorAll('#add-address-btn, #add-first-address-btn, .edit-address');
|
|
649
666
|
const closeButtons = document.querySelectorAll('[data-address-modal-close]');
|
|
@@ -651,6 +668,36 @@
|
|
|
651
668
|
const addresses = {% if addresses %}{{ addresses | json }}{% else %}[]{% endif %};
|
|
652
669
|
const COUNTRIES_DATA = {% if countries %}{{ countries | json }}{% else %}[]{% endif %};
|
|
653
670
|
let addressPhoneIti = null;
|
|
671
|
+
let intlTelInputLoadPromise = null;
|
|
672
|
+
|
|
673
|
+
function ensureIntlTelInputLoaded() {
|
|
674
|
+
if (typeof window.intlTelInput !== 'undefined') {
|
|
675
|
+
return Promise.resolve(true);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (intlTelInputLoadPromise) {
|
|
679
|
+
return intlTelInputLoadPromise;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
intlTelInputLoadPromise = new Promise((resolve) => {
|
|
683
|
+
const existingScript = document.querySelector('script[data-intl-tel-input-loader="address-book"]');
|
|
684
|
+
if (existingScript) {
|
|
685
|
+
existingScript.addEventListener('load', () => resolve(typeof window.intlTelInput !== 'undefined'), { once: true });
|
|
686
|
+
existingScript.addEventListener('error', () => resolve(false), { once: true });
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const script = document.createElement('script');
|
|
691
|
+
script.src = 'https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/js/intlTelInput.min.js';
|
|
692
|
+
script.defer = true;
|
|
693
|
+
script.setAttribute('data-intl-tel-input-loader', 'address-book');
|
|
694
|
+
script.onload = () => resolve(typeof window.intlTelInput !== 'undefined');
|
|
695
|
+
script.onerror = () => resolve(false);
|
|
696
|
+
document.head.appendChild(script);
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
return intlTelInputLoadPromise;
|
|
700
|
+
}
|
|
654
701
|
|
|
655
702
|
// Helper function to get states for a country
|
|
656
703
|
function getStatesForCountry(countryIdentifier) {
|
|
@@ -750,8 +797,24 @@
|
|
|
750
797
|
stateSelect.style.display = 'block';
|
|
751
798
|
stateSelect.required = true;
|
|
752
799
|
|
|
800
|
+
// De-duplicate states to avoid repeated options when upstream data contains duplicates.
|
|
801
|
+
const seenStateKeys = new Set();
|
|
802
|
+
const uniqueStates = [];
|
|
803
|
+
countryStates.forEach((state) => {
|
|
804
|
+
const stateCode = state && (state.code || state.abbreviation || state.isoCode || '');
|
|
805
|
+
const stateName = state && (state.name || state.label || '');
|
|
806
|
+
const normalizedCode = String(stateCode).trim().toLowerCase();
|
|
807
|
+
const normalizedName = String(stateName).trim().toLowerCase().replace(/\s+/g, ' ');
|
|
808
|
+
const key = normalizedCode ? `code:${normalizedCode}` : `name:${normalizedName}`;
|
|
809
|
+
|
|
810
|
+
if (!seenStateKeys.has(key)) {
|
|
811
|
+
seenStateKeys.add(key);
|
|
812
|
+
uniqueStates.push(state);
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
|
|
753
816
|
// Populate the dropdown
|
|
754
|
-
|
|
817
|
+
uniqueStates.forEach(state => {
|
|
755
818
|
const option = document.createElement('option');
|
|
756
819
|
const stateId = state.id || state.stateOrProvinceId || state.stateId;
|
|
757
820
|
const stateName = state.name || state.label || '';
|
|
@@ -790,7 +853,15 @@
|
|
|
790
853
|
// Initialize intl-tel-input for address phone
|
|
791
854
|
function initializeAddressPhoneInput() {
|
|
792
855
|
const phoneInput = document.getElementById('address-phone');
|
|
793
|
-
if (phoneInput
|
|
856
|
+
if (!phoneInput) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
ensureIntlTelInputLoaded().then((isLoaded) => {
|
|
861
|
+
if (!isLoaded || typeof window.intlTelInput === 'undefined') {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
|
|
794
865
|
// Destroy existing instance if any
|
|
795
866
|
if (addressPhoneIti) {
|
|
796
867
|
addressPhoneIti.destroy();
|
|
@@ -799,30 +870,38 @@
|
|
|
799
870
|
|
|
800
871
|
// Get country code from country select
|
|
801
872
|
const countrySelect = document.getElementById('address-country');
|
|
802
|
-
let initialCountry = '
|
|
873
|
+
let initialCountry = 'us';
|
|
803
874
|
|
|
804
875
|
if (countrySelect && countrySelect.value) {
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
876
|
+
const selectedCountryCode = (countrySelect.options[countrySelect.selectedIndex]?.getAttribute('data-country-code2') || '').trim().toLowerCase();
|
|
877
|
+
if (/^[a-z]{2}$/.test(selectedCountryCode)) {
|
|
878
|
+
initialCountry = selectedCountryCode;
|
|
879
|
+
}
|
|
808
880
|
}
|
|
809
881
|
|
|
810
882
|
// Initialize intl-tel-input
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
883
|
+
try {
|
|
884
|
+
addressPhoneIti = window.intlTelInput(phoneInput, {
|
|
885
|
+
utilsScript: 'https://cdn.jsdelivr.net/npm/intl-tel-input@23.0.0/build/js/utils.js',
|
|
886
|
+
initialCountry: initialCountry,
|
|
887
|
+
preferredCountries: ['us', 'gb', 'ca', 'au', 'in'],
|
|
888
|
+
separateDialCode: true,
|
|
889
|
+
nationalMode: false,
|
|
890
|
+
dropdownContainer: document.body
|
|
891
|
+
});
|
|
892
|
+
} catch (error) {
|
|
893
|
+
console.error('[ADDRESS BOOK] Failed to initialize phone country selector', error);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
window.addressPhoneIti = addressPhoneIti;
|
|
818
897
|
|
|
819
898
|
// Update country when address country changes
|
|
820
|
-
if (countrySelect) {
|
|
821
|
-
countrySelect.
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (addressPhoneIti && countryCode) {
|
|
825
|
-
addressPhoneIti.setCountry(countryCode
|
|
899
|
+
if (countrySelect && !countrySelect.dataset.phoneCountrySyncBound) {
|
|
900
|
+
countrySelect.dataset.phoneCountrySyncBound = '1';
|
|
901
|
+
countrySelect.addEventListener('change', () => {
|
|
902
|
+
const countryCode = (countrySelect.options[countrySelect.selectedIndex]?.getAttribute('data-country-code2') || '').trim().toLowerCase();
|
|
903
|
+
if (addressPhoneIti && /^[a-z]{2}$/.test(countryCode)) {
|
|
904
|
+
addressPhoneIti.setCountry(countryCode);
|
|
826
905
|
}
|
|
827
906
|
|
|
828
907
|
// Populate states when country changes
|
|
@@ -832,7 +911,7 @@
|
|
|
832
911
|
}
|
|
833
912
|
});
|
|
834
913
|
}
|
|
835
|
-
}
|
|
914
|
+
});
|
|
836
915
|
}
|
|
837
916
|
|
|
838
917
|
function openModal(addressId = null) {
|
|
@@ -902,11 +981,11 @@
|
|
|
902
981
|
document.body.style.overflow = 'hidden';
|
|
903
982
|
|
|
904
983
|
// Initialize phone input after modal is shown
|
|
905
|
-
setTimeout(
|
|
984
|
+
setTimeout(() => {
|
|
906
985
|
initializeAddressPhoneInput();
|
|
907
986
|
// Set phone number if editing (after initialization completes)
|
|
908
987
|
if (addressId) {
|
|
909
|
-
setTimeout(
|
|
988
|
+
setTimeout(() => {
|
|
910
989
|
const address = addresses.find(a => a.id == addressId);
|
|
911
990
|
if (address && address.phone && addressPhoneIti) {
|
|
912
991
|
addressPhoneIti.setNumber(address.phone);
|
|
@@ -925,11 +1004,13 @@
|
|
|
925
1004
|
addressPhoneIti.destroy();
|
|
926
1005
|
addressPhoneIti = null;
|
|
927
1006
|
}
|
|
1007
|
+
window.addressPhoneIti = null;
|
|
928
1008
|
}
|
|
929
1009
|
|
|
930
1010
|
openButtons.forEach(btn => {
|
|
931
|
-
btn.addEventListener('click',
|
|
932
|
-
const
|
|
1011
|
+
btn.addEventListener('click', (event) => {
|
|
1012
|
+
const targetButton = event.currentTarget || btn;
|
|
1013
|
+
const addressId = targetButton.dataset.addressId || null;
|
|
933
1014
|
openModal(addressId);
|
|
934
1015
|
});
|
|
935
1016
|
});
|
|
@@ -939,7 +1020,7 @@
|
|
|
939
1020
|
});
|
|
940
1021
|
|
|
941
1022
|
// Handle form submission
|
|
942
|
-
form.addEventListener('submit', async
|
|
1023
|
+
form.addEventListener('submit', async (e) => {
|
|
943
1024
|
e.preventDefault();
|
|
944
1025
|
|
|
945
1026
|
const submitBtn = document.getElementById('save-address-btn');
|
|
@@ -1060,33 +1141,49 @@
|
|
|
1060
1141
|
|
|
1061
1142
|
// Handle delete
|
|
1062
1143
|
document.querySelectorAll('.delete-address').forEach(btn => {
|
|
1063
|
-
btn.addEventListener('click', async
|
|
1144
|
+
btn.addEventListener('click', async (event) => {
|
|
1145
|
+
event.preventDefault();
|
|
1064
1146
|
if (confirm('Are you sure you want to delete this address?')) {
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1147
|
+
const targetButton = event.currentTarget || btn;
|
|
1148
|
+
const addressId = targetButton.dataset.addressId;
|
|
1149
|
+
if (!addressId) {
|
|
1150
|
+
alert('Invalid address. Please refresh and try again.');
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
setButtonLoading(targetButton, true, 'Deleting...');
|
|
1067
1154
|
|
|
1068
1155
|
try {
|
|
1156
|
+
|
|
1157
|
+
|
|
1069
1158
|
const response = await fetch(`/webstoreapi/addresses/${addressId}`, {
|
|
1159
|
+
|
|
1070
1160
|
method: 'DELETE',
|
|
1071
1161
|
headers: {
|
|
1162
|
+
|
|
1072
1163
|
'X-Requested-With': 'XMLHttpRequest'
|
|
1073
1164
|
}
|
|
1074
1165
|
});
|
|
1166
|
+
|
|
1075
1167
|
|
|
1076
1168
|
if (response.ok) {
|
|
1077
1169
|
window.location.reload();
|
|
1078
1170
|
} else {
|
|
1079
|
-
|
|
1080
|
-
|
|
1171
|
+
const error = await response.json();
|
|
1172
|
+
|
|
1173
|
+
setButtonLoading(targetButton, false, 'Delete');
|
|
1081
1174
|
alert('Error deleting address: ' + (error.error || error.message || 'Unknown error'));
|
|
1082
1175
|
}
|
|
1083
1176
|
} catch (error) {
|
|
1084
1177
|
console.error('Error deleting address:', error);
|
|
1085
|
-
setButtonLoading(
|
|
1086
|
-
|
|
1178
|
+
setButtonLoading(targetButton, false, 'Delete');
|
|
1179
|
+
if (error.name === 'AbortError') {
|
|
1180
|
+
alert('Delete request timed out. Please try again.');
|
|
1181
|
+
} else {
|
|
1182
|
+
alert('Error deleting address. Please try again.');
|
|
1183
|
+
}
|
|
1087
1184
|
}
|
|
1088
1185
|
}
|
|
1089
1186
|
});
|
|
1090
1187
|
});
|
|
1091
1188
|
});
|
|
1092
|
-
</script>
|
|
1189
|
+
</script>
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
{% layout 'layout/theme' %}
|
|
2
2
|
|
|
3
|
+
<!-- Hero Widgets (if any) -->
|
|
4
|
+
{% assign hero_widgets = widgets.hero %}
|
|
5
|
+
{% if hero_widgets and hero_widgets.size > 0 %}
|
|
6
|
+
<section class="theme-section theme-section--hero" data-section="hero">
|
|
7
|
+
{% for widget in hero_widgets %}
|
|
8
|
+
<div class="theme-widget-wrapper"
|
|
9
|
+
data-widget-id="{{ widget.id }}"
|
|
10
|
+
data-widget-type="{{ widget.type }}"
|
|
11
|
+
data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
|
|
12
|
+
{% if widget and widget.template_path %}
|
|
13
|
+
{% render widget.template_path, widget: widget, settings: settings, shop: shop, is_hero_first: forloop.first %}
|
|
14
|
+
{% endif %}
|
|
15
|
+
</div>
|
|
16
|
+
{% endfor %}
|
|
17
|
+
</section>
|
|
18
|
+
{% endif %}
|
|
19
|
+
|
|
3
20
|
<div class="collections-page">
|
|
4
21
|
<div class="collections-header">
|
|
5
|
-
<h1 class="page-title">Our
|
|
6
|
-
<p class="page-description">Discover our curated
|
|
22
|
+
<h1 class="page-title">Our Categories</h1>
|
|
23
|
+
<p class="page-description">Discover our curated categories of amazing products</p>
|
|
7
24
|
</div>
|
|
8
25
|
<div class="collections-content">
|
|
9
|
-
{% if
|
|
26
|
+
{% if categories.size > 0 %}
|
|
10
27
|
<div class="collections-grid">
|
|
11
|
-
{% for
|
|
28
|
+
{% for category in categories %}
|
|
12
29
|
<div class="collection-card">
|
|
13
|
-
<a href="/{{
|
|
30
|
+
<a href="/{{ category.slug }}" class="collection-link">
|
|
14
31
|
<div class="collection-image">
|
|
15
|
-
{% if
|
|
16
|
-
<img src="{{
|
|
17
|
-
alt="{{
|
|
32
|
+
{% if category.image %}
|
|
33
|
+
<img src="{{ category.image }}"
|
|
34
|
+
alt="{{ category.title | default: category.name }}"
|
|
18
35
|
width="100%"
|
|
19
36
|
height="200"
|
|
20
37
|
loading="lazy">
|
|
@@ -35,9 +52,9 @@
|
|
|
35
52
|
{% endif %}
|
|
36
53
|
</div>
|
|
37
54
|
<div class="collection-info">
|
|
38
|
-
<h3 class="collection-title">{{
|
|
39
|
-
{% if
|
|
40
|
-
<p class="collection-description">{{
|
|
55
|
+
<h3 class="collection-title">{{ category.title | default: category.name }}</h3>
|
|
56
|
+
{% if category.description %}
|
|
57
|
+
<p class="collection-description">{{ category.description }}</p>
|
|
41
58
|
{% else %}
|
|
42
59
|
<p class="collection-description">Setup your products and images in Catalog - Category</p>
|
|
43
60
|
{% endif %}
|
|
@@ -47,12 +64,12 @@
|
|
|
47
64
|
{% endfor %}
|
|
48
65
|
</div>
|
|
49
66
|
{% else %}
|
|
50
|
-
<!-- No
|
|
67
|
+
<!-- No Categories -->
|
|
51
68
|
<div class="no-collections">
|
|
52
69
|
<div class="no-collections-content">
|
|
53
70
|
<div class="no-collections-icon">📂</div>
|
|
54
|
-
<h2>No
|
|
55
|
-
<p>We're working on creating amazing
|
|
71
|
+
<h2>No Categories Available</h2>
|
|
72
|
+
<p>We're working on creating amazing categories for you. Check back soon!</p>
|
|
56
73
|
<a href="/products" class="btn btn-primary">Browse All Products</a>
|
|
57
74
|
</div>
|
|
58
75
|
</div>
|
|
@@ -65,11 +82,55 @@
|
|
|
65
82
|
</div>
|
|
66
83
|
</div>
|
|
67
84
|
|
|
85
|
+
<!-- Content Widgets (if any) -->
|
|
86
|
+
{% assign content_widgets = widgets.content %}
|
|
87
|
+
{% if content_widgets and content_widgets.size > 0 %}
|
|
88
|
+
<section class="theme-section theme-section--content" data-section="content">
|
|
89
|
+
{% for widget in content_widgets %}
|
|
90
|
+
<div class="theme-widget-wrapper"
|
|
91
|
+
data-widget-id="{{ widget.id }}"
|
|
92
|
+
data-widget-type="{{ widget.type }}"
|
|
93
|
+
data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
|
|
94
|
+
{% if widget and widget.template_path %}
|
|
95
|
+
{% render widget.template_path, widget: widget, settings: settings, shop: shop %}
|
|
96
|
+
{% endif %}
|
|
97
|
+
</div>
|
|
98
|
+
{% endfor %}
|
|
99
|
+
</section>
|
|
100
|
+
{% endif %}
|
|
101
|
+
|
|
68
102
|
<style>
|
|
103
|
+
/* Template-level variable mappings to global theme variables */
|
|
104
|
+
:root {
|
|
105
|
+
--color-primary: {{ settings.color_primary | default: '#000000' }};
|
|
106
|
+
--color-primary-hover: {{ settings.color_primary_dark | default: '#333333' }};
|
|
107
|
+
--color-primary-light: {{ settings.color_primary_light | default: '#666666' }};
|
|
108
|
+
--color-success: {{ settings.color_success | default: '#22c55e' }};
|
|
109
|
+
--color-success-light: {{ settings.color_success | default: '#22c55e' }};
|
|
110
|
+
--color-danger: {{ settings.color_error | default: '#ef4444' }};
|
|
111
|
+
--color-danger-light: {{ settings.color_error | default: '#ef4444' }};
|
|
112
|
+
--color-text: {{ settings.color_text | default: '#000000' }};
|
|
113
|
+
--color-text-light: {{ settings.color_text_muted | default: '#666666' }};
|
|
114
|
+
--color-background: {{ settings.color_background | default: '#ffffff' }};
|
|
115
|
+
--color-card-bg: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
116
|
+
--color-border: {{ settings.color_border | default: '#cccccc' }};
|
|
117
|
+
--color-border-light: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
118
|
+
--shadow-sm: var(--shadow-sm);
|
|
119
|
+
--shadow-md: var(--shadow-md);
|
|
120
|
+
--shadow-lg: var(--shadow-lg);
|
|
121
|
+
--radius-md: var(--radius-md);
|
|
122
|
+
--radius-lg: var(--radius-lg);
|
|
123
|
+
--spacing-xs: var(--space-3);
|
|
124
|
+
--spacing-sm: var(--space-4);
|
|
125
|
+
--spacing-md: var(--space-6);
|
|
126
|
+
--spacing-lg: var(--space-8);
|
|
127
|
+
--spacing-xl: var(--space-12);
|
|
128
|
+
}
|
|
129
|
+
|
|
69
130
|
/* Collection Toolbar Styles */
|
|
70
131
|
.collection-toolbar {
|
|
71
|
-
background-color:
|
|
72
|
-
border-bottom: 1px solid
|
|
132
|
+
background-color: var(--color-white);
|
|
133
|
+
border-bottom: 1px solid var(--color-border);
|
|
73
134
|
padding: 1.5rem 0;
|
|
74
135
|
margin-bottom: 2rem;
|
|
75
136
|
}
|
|
@@ -84,7 +145,7 @@
|
|
|
84
145
|
padding: 50px 20px;
|
|
85
146
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
86
147
|
color: white;
|
|
87
|
-
border-radius:
|
|
148
|
+
border-radius: var(--border-radius-medium);
|
|
88
149
|
}
|
|
89
150
|
|
|
90
151
|
.page-title {
|
|
@@ -114,10 +175,8 @@
|
|
|
114
175
|
|
|
115
176
|
.collection-card {
|
|
116
177
|
background: white;
|
|
117
|
-
border-radius:
|
|
118
|
-
overflow: hidden;
|
|
119
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
120
|
-
transition: box-shadow 0.3s ease;
|
|
178
|
+
border-radius: var(--border-radius-medium);
|
|
179
|
+
overflow: hidden;
|
|
121
180
|
position: relative;
|
|
122
181
|
display: flex;
|
|
123
182
|
flex-direction: column;
|
|
@@ -125,9 +184,9 @@
|
|
|
125
184
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
126
185
|
}
|
|
127
186
|
|
|
128
|
-
.collection-card:hover {
|
|
187
|
+
{% comment %} .collection-card:hover {
|
|
129
188
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
130
|
-
}
|
|
189
|
+
} {% endcomment %}
|
|
131
190
|
|
|
132
191
|
.collection-link {
|
|
133
192
|
text-decoration: none;
|
|
@@ -147,6 +206,7 @@
|
|
|
147
206
|
display: flex;
|
|
148
207
|
align-items: center;
|
|
149
208
|
justify-content: center;
|
|
209
|
+
border-radius: var(--border-radius-medium);
|
|
150
210
|
}
|
|
151
211
|
|
|
152
212
|
.collection-image img {
|
|
@@ -214,7 +274,7 @@
|
|
|
214
274
|
}
|
|
215
275
|
|
|
216
276
|
.collection-title {
|
|
217
|
-
font-size:
|
|
277
|
+
font-size: var(--text-xs);
|
|
218
278
|
margin-bottom: 8px;
|
|
219
279
|
color: #000;
|
|
220
280
|
font-weight: 700;
|
|
@@ -320,7 +380,7 @@
|
|
|
320
380
|
</style>
|
|
321
381
|
|
|
322
382
|
<script>
|
|
323
|
-
document.addEventListener('DOMContentLoaded',
|
|
383
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
324
384
|
// Parse URL parameters
|
|
325
385
|
const urlParams = new URLSearchParams(window.location.search);
|
|
326
386
|
|
|
@@ -389,7 +449,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
389
449
|
|
|
390
450
|
// Toggle sort dropdown
|
|
391
451
|
if (sortToggle && sortMenu) {
|
|
392
|
-
sortToggle.addEventListener('click',
|
|
452
|
+
sortToggle.addEventListener('click', (e) => {
|
|
393
453
|
e.stopPropagation();
|
|
394
454
|
sortMenu.classList.toggle('active');
|
|
395
455
|
sortToggle.setAttribute('aria-expanded', sortMenu.classList.contains('active'));
|
|
@@ -397,7 +457,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
397
457
|
}
|
|
398
458
|
|
|
399
459
|
// Close sort dropdown when clicking outside
|
|
400
|
-
document.addEventListener('click',
|
|
460
|
+
document.addEventListener('click', (e) => {
|
|
401
461
|
if (sortDropdown && !sortDropdown.contains(e.target)) {
|
|
402
462
|
sortMenu.classList.remove('active');
|
|
403
463
|
sortToggle.setAttribute('aria-expanded', 'false');
|
|
@@ -408,7 +468,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
408
468
|
if (sortMenu) {
|
|
409
469
|
const sortOptions = sortMenu.querySelectorAll('.sort-option');
|
|
410
470
|
sortOptions.forEach(option => {
|
|
411
|
-
option.addEventListener('click',
|
|
471
|
+
option.addEventListener('click', () => {
|
|
412
472
|
const sortValue = this.getAttribute('data-sort');
|
|
413
473
|
const orderValue = this.getAttribute('data-order');
|
|
414
474
|
const label = this.getAttribute('data-label');
|
|
@@ -439,11 +499,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
439
499
|
const collectionCards = document.querySelectorAll('.collection-card');
|
|
440
500
|
|
|
441
501
|
collectionCards.forEach(card => {
|
|
442
|
-
card.addEventListener('mouseenter',
|
|
502
|
+
card.addEventListener('mouseenter', () => {
|
|
443
503
|
this.style.transform = 'translateY(-8px)';
|
|
444
504
|
});
|
|
445
505
|
|
|
446
|
-
card.addEventListener('mouseleave',
|
|
506
|
+
card.addEventListener('mouseleave', () => {
|
|
447
507
|
this.style.transform = 'translateY(0)';
|
|
448
508
|
});
|
|
449
509
|
});
|