@o2vend/theme-cli 1.0.32

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 (116) hide show
  1. package/README.md +425 -0
  2. package/assets/Logo_o2vend.png +0 -0
  3. package/assets/favicon.png +0 -0
  4. package/assets/logo-white.png +0 -0
  5. package/bin/o2vend +42 -0
  6. package/config/widget-map.json +50 -0
  7. package/lib/commands/check.js +201 -0
  8. package/lib/commands/generate.js +33 -0
  9. package/lib/commands/init.js +214 -0
  10. package/lib/commands/optimize.js +216 -0
  11. package/lib/commands/package.js +208 -0
  12. package/lib/commands/serve.js +105 -0
  13. package/lib/commands/validate.js +191 -0
  14. package/lib/lib/api-client.js +357 -0
  15. package/lib/lib/dev-server.js +2618 -0
  16. package/lib/lib/file-watcher.js +80 -0
  17. package/lib/lib/hot-reload.js +106 -0
  18. package/lib/lib/liquid-engine.js +822 -0
  19. package/lib/lib/liquid-filters.js +671 -0
  20. package/lib/lib/mock-api-server.js +989 -0
  21. package/lib/lib/mock-data.js +1468 -0
  22. package/lib/lib/widget-service.js +321 -0
  23. package/package.json +70 -0
  24. package/test-theme/README.md +27 -0
  25. package/test-theme/assets/async-sections.js +446 -0
  26. package/test-theme/assets/cart-drawer.js +463 -0
  27. package/test-theme/assets/cart-manager.js +223 -0
  28. package/test-theme/assets/checkout-price-handler.js +368 -0
  29. package/test-theme/assets/components.css +4629 -0
  30. package/test-theme/assets/delivery-zone.css +299 -0
  31. package/test-theme/assets/delivery-zone.js +396 -0
  32. package/test-theme/assets/logo.png +0 -0
  33. package/test-theme/assets/sections.css +48 -0
  34. package/test-theme/assets/theme.css +3500 -0
  35. package/test-theme/assets/theme.js +3745 -0
  36. package/test-theme/config/settings_data.json +292 -0
  37. package/test-theme/config/settings_schema.json +1050 -0
  38. package/test-theme/layout/theme.liquid +195 -0
  39. package/test-theme/locales/en.default.json +260 -0
  40. package/test-theme/sections/content-fallback.liquid +53 -0
  41. package/test-theme/sections/content.liquid +57 -0
  42. package/test-theme/sections/footer-fallback.liquid +328 -0
  43. package/test-theme/sections/footer.liquid +278 -0
  44. package/test-theme/sections/header-fallback.liquid +1805 -0
  45. package/test-theme/sections/header.liquid +1145 -0
  46. package/test-theme/sections/hero-fallback.liquid +212 -0
  47. package/test-theme/sections/hero.liquid +136 -0
  48. package/test-theme/snippets/account-sidebar.liquid +200 -0
  49. package/test-theme/snippets/add-to-cart-modal.liquid +484 -0
  50. package/test-theme/snippets/breadcrumbs.liquid +134 -0
  51. package/test-theme/snippets/cart-drawer.liquid +467 -0
  52. package/test-theme/snippets/delivery-zone-city-selector.liquid +79 -0
  53. package/test-theme/snippets/delivery-zone-modal.liquid +337 -0
  54. package/test-theme/snippets/delivery-zone-search.liquid +78 -0
  55. package/test-theme/snippets/icon.liquid +105 -0
  56. package/test-theme/snippets/login-modal.liquid +346 -0
  57. package/test-theme/snippets/mega-menu.liquid +812 -0
  58. package/test-theme/snippets/news-thumbnail.liquid +187 -0
  59. package/test-theme/snippets/pagination.liquid +120 -0
  60. package/test-theme/snippets/price.liquid +92 -0
  61. package/test-theme/snippets/product-card-related.liquid +78 -0
  62. package/test-theme/snippets/product-card-simple.liquid +41 -0
  63. package/test-theme/snippets/product-card.liquid +697 -0
  64. package/test-theme/snippets/rating.liquid +85 -0
  65. package/test-theme/snippets/skeleton-collection-grid.liquid +114 -0
  66. package/test-theme/snippets/skeleton-product-card.liquid +124 -0
  67. package/test-theme/snippets/skeleton-product-grid.liquid +34 -0
  68. package/test-theme/snippets/social-sharing.liquid +185 -0
  69. package/test-theme/templates/account/dashboard.liquid +401 -0
  70. package/test-theme/templates/account/loyalty-redemption.liquid +405 -0
  71. package/test-theme/templates/account/loyalty.liquid +588 -0
  72. package/test-theme/templates/account/order-detail.liquid +230 -0
  73. package/test-theme/templates/account/orders.liquid +349 -0
  74. package/test-theme/templates/account/profile.liquid +758 -0
  75. package/test-theme/templates/account/register.liquid +232 -0
  76. package/test-theme/templates/account/return-orders.liquid +348 -0
  77. package/test-theme/templates/account/store-credit.liquid +464 -0
  78. package/test-theme/templates/account/subscriptions.liquid +601 -0
  79. package/test-theme/templates/account/wishlist.liquid +419 -0
  80. package/test-theme/templates/address-book.liquid +1092 -0
  81. package/test-theme/templates/categories.liquid +452 -0
  82. package/test-theme/templates/checkout.liquid +4511 -0
  83. package/test-theme/templates/error.liquid +384 -0
  84. package/test-theme/templates/index.liquid +11 -0
  85. package/test-theme/templates/login.liquid +185 -0
  86. package/test-theme/templates/order-confirmation.liquid +720 -0
  87. package/test-theme/templates/page.liquid +297 -0
  88. package/test-theme/templates/product-detail.liquid +4363 -0
  89. package/test-theme/templates/products.liquid +518 -0
  90. package/test-theme/templates/search.liquid +922 -0
  91. package/test-theme/theme.json.example +19 -0
  92. package/test-theme/widgets/brand-carousel.liquid +676 -0
  93. package/test-theme/widgets/brand.liquid +245 -0
  94. package/test-theme/widgets/carousel.liquid +843 -0
  95. package/test-theme/widgets/category-list-carousel.liquid +656 -0
  96. package/test-theme/widgets/category-list.liquid +340 -0
  97. package/test-theme/widgets/category.liquid +475 -0
  98. package/test-theme/widgets/discount-time.liquid +176 -0
  99. package/test-theme/widgets/footer-menu.liquid +695 -0
  100. package/test-theme/widgets/footer.liquid +179 -0
  101. package/test-theme/widgets/gallery.liquid +271 -0
  102. package/test-theme/widgets/header-menu.liquid +932 -0
  103. package/test-theme/widgets/header.liquid +159 -0
  104. package/test-theme/widgets/html.liquid +214 -0
  105. package/test-theme/widgets/news.liquid +217 -0
  106. package/test-theme/widgets/product-canvas.liquid +235 -0
  107. package/test-theme/widgets/product-carousel.liquid +502 -0
  108. package/test-theme/widgets/product.liquid +45 -0
  109. package/test-theme/widgets/recently-viewed.liquid +26 -0
  110. package/test-theme/widgets/shared/product-grid.liquid +339 -0
  111. package/test-theme/widgets/simple-product.liquid +42 -0
  112. package/test-theme/widgets/single-product.liquid +610 -0
  113. package/test-theme/widgets/spacebar-carousel.liquid +663 -0
  114. package/test-theme/widgets/spacebar.liquid +279 -0
  115. package/test-theme/widgets/splash.liquid +378 -0
  116. package/test-theme/widgets/testimonial-carousel.liquid +709 -0
@@ -0,0 +1,1145 @@
1
+ {% comment %}
2
+ Header Section - Fixed Layout Issues
3
+
4
+ Key fixes:
5
+ 1. Hamburger menu now properly visible on mobile
6
+ 2. Improved responsive layout
7
+ 3. Better cart button styling
8
+ 4. Fixed search bar positioning
9
+ 5. Proper mobile/desktop breakpoints
10
+ {% endcomment %}
11
+
12
+ {% assign header_widgets = widgets.header %}
13
+ {% assign header_menu_widget = null %}
14
+ {% if header_widgets and header_widgets.size > 0 %}
15
+ {% for widget in header_widgets %}
16
+ {% assign widget_type = widget.type | default: '' | strip %}
17
+ {% if widget_type == 'HeaderMenu' %}
18
+ {% assign header_menu_widget = widget %}
19
+ {% break %}
20
+ {% endif %}
21
+ {% endfor %}
22
+ {% endif %}
23
+
24
+ {% liquid
25
+ assign widget_settings = header_menu_widget.settings | default: section.settings
26
+ assign widget_data = header_menu_widget.data | default: null
27
+
28
+ assign content_data = widget_data.content | default: null
29
+ if content_data and content_data.size > 0
30
+ assign header_config = content_data.first
31
+ else
32
+ assign header_config = content_data
33
+ endif
34
+
35
+ assign logo_url = nil
36
+ if widget_settings.logo and widget_settings.logo != blank
37
+ assign logo_url = widget_settings.logo
38
+ elsif shop.logo and shop.logo != blank
39
+ assign logo_url = shop.logo
40
+ else
41
+ assign logo_url = '/themes/default/assets/logo.png'
42
+ endif
43
+ assign logo_alt = widget_settings.logoAlt | default: shop.name
44
+ assign logo_link = widget_settings.logoLink | default: '/'
45
+
46
+ assign show_search = widget_settings.showSearch
47
+ if show_search == nil and header_config and header_config.ShowSearch != nil
48
+ assign show_search = header_config.ShowSearch
49
+ endif
50
+ if show_search == nil
51
+ assign show_search = true
52
+ endif
53
+
54
+ assign show_cart = widget_settings.showCart
55
+ if show_cart == nil and header_config and header_config.ShowCart != nil
56
+ assign show_cart = header_config.ShowCart
57
+ endif
58
+ if show_cart == nil
59
+ assign show_cart = true
60
+ endif
61
+
62
+ assign show_account = widget_settings.showAccount
63
+ if show_account == nil and header_config and header_config.ShowAccount != nil
64
+ assign show_account = header_config.ShowAccount
65
+ endif
66
+ if show_account == nil
67
+ assign show_account = true
68
+ endif
69
+ %}
70
+
71
+ <header class="site-header" data-section-id="{{ section.id }}">
72
+ <div class="site-header__container">
73
+ {% comment %}HeaderWidget widgets (announcement bar, etc.){% endcomment %}
74
+ {% if header_widgets and header_widgets.size > 0 %}
75
+ {% for widget in header_widgets %}
76
+ {% assign widget_type = widget.type | default: '' | downcase %}
77
+ {% assign widget_type_normalized = widget_type | strip %}
78
+ {% if widget_type_normalized == 'headerwidget' or widget_type_normalized == 'header' %}
79
+ <div class="theme-widget-wrapper theme-widget-wrapper--header-widget"
80
+ data-widget-id="{{ widget.id }}"
81
+ data-widget-type="{{ widget.type }}"
82
+ data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
83
+ {% if widget and widget.template_path %}
84
+ {% render widget.template_path, widget: widget, settings: settings %}
85
+ {% else %}
86
+ <div class="widget-error" data-widget-error>
87
+ <p>Widget template not found: {{ widget.template_path | default: 'unknown' }}</p>
88
+ </div>
89
+ {% endif %}
90
+ </div>
91
+ {% endif %}
92
+ {% endfor %}
93
+ {% endif %}
94
+
95
+ {% comment %}Top Row: Logo, Search, Account/Cart{% endcomment %}
96
+ <div class="site-header__top-row">
97
+ {% comment %}Hamburger Menu - Mobile Only{% endcomment %}
98
+ <button class="site-header__toggle" aria-label="Toggle navigation" data-header-menu-toggle type="button">
99
+ <span></span>
100
+ <span></span>
101
+ <span></span>
102
+ </button>
103
+
104
+ <div class="site-header__logo">
105
+ <a href="{{ logo_link }}" aria-label="{{ logo_alt }}">
106
+ {% if logo_url %}
107
+ <img src="{{ logo_url }}" alt="{{ logo_alt }}" width="200" height="28" class="site-header__logo-image">
108
+ {% else %}
109
+ <span class="site-header__brand">{{ shop.name }}</span>
110
+ {% endif %}
111
+ </a>
112
+ </div>
113
+
114
+ {% comment %}Desktop Search{% endcomment %}
115
+ <div class="site-header__search-wrapper">
116
+ {% if show_search %}
117
+ <div class="site-header__search-container" data-search-container>
118
+ <form class="site-header__search-form" action="/search" method="get" role="search">
119
+ <div class="site-header__search-input-wrapper">
120
+ <svg class="site-header__search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
121
+ <circle cx="11" cy="11" r="8"></circle>
122
+ <path d="m21 21-4.35-4.35"></path>
123
+ </svg>
124
+ <input
125
+ type="search"
126
+ name="q"
127
+ class="site-header__search-input"
128
+ placeholder="{{ widget_settings.search_placeholder | default: 'Search for...' }}"
129
+ aria-label="Search products"
130
+ autocomplete="off"
131
+ data-search-input>
132
+ <button type="button" class="site-header__search-clear" aria-label="Clear search" data-search-clear style="display: none;">
133
+ <svg class="site-header__search-clear-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
134
+ <line x1="18" y1="6" x2="6" y2="18"></line>
135
+ <line x1="6" y1="6" x2="18" y2="18"></line>
136
+ </svg>
137
+ </button>
138
+ </div>
139
+ </form>
140
+ <div class="site-header__search-dropdown" data-search-dropdown style="display: none;">
141
+ <div class="site-header__search-loading" data-search-loading style="display: none;">
142
+ <div class="site-header__search-spinner"></div>
143
+ <span>Searching...</span>
144
+ </div>
145
+ <div class="site-header__search-results" data-search-results></div>
146
+ <div class="site-header__search-empty" data-search-empty style="display: none;">
147
+ <p>No products found</p>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ {% endif %}
152
+ </div>
153
+
154
+ <div class="site-header__actions">
155
+ {% if show_account %}
156
+ {% comment %}Debug: customer={{ customer | json }}{% endcomment %}
157
+ {% if customer and customer.isAuthenticated %}
158
+ <a href="/account" class="site-header__action site-header__action--account" aria-label="Account">
159
+ <svg class="site-header__account-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
160
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
161
+ <circle cx="12" cy="7" r="4"></circle>
162
+ </svg>
163
+ </a>
164
+ <form method="post" action="/webstoreapi/customer/logout" class="site-header__logout-form">
165
+ <button type="submit" class="site-header__action site-header__action--logout" aria-label="Logout">
166
+ <svg class="site-header__logout-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
167
+ <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
168
+ <polyline points="16 17 21 12 16 7"></polyline>
169
+ <line x1="21" y1="12" x2="9" y2="12"></line>
170
+ </svg>
171
+ </button>
172
+ </form>
173
+ {% else %}
174
+ <a href="#" class="site-header__action site-header__action--login" aria-label="Login" data-login-modal-trigger>
175
+ <svg class="site-header__login-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
176
+ <circle cx="12" cy="8" r="4"></circle>
177
+ <path d="M6 20a6 6 0 0 1 12 0"></path>
178
+ </svg>
179
+ </a>
180
+ {% endif %}
181
+ {% endif %}
182
+
183
+ {% if show_cart %}
184
+ <button class="site-header__action site-header__action--cart" aria-label="Shopping cart" data-cart-toggle type="button">
185
+ <svg class="site-header__cart-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
186
+ <circle cx="9" cy="21" r="1"></circle>
187
+ <circle cx="20" cy="21" r="1"></circle>
188
+ <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
189
+ </svg>
190
+ <span class="site-header__cart-text" style="display: none;"></span>
191
+ <span class="site-header__cart-count" data-cart-count="{% if cart.itemCount and cart.itemCount > 0 %}{{ cart.itemCount }}{% else %}0{% endif %}" {% unless cart.itemCount and cart.itemCount > 0 %}style="display: none;"{% endunless %}>{% if cart.itemCount and cart.itemCount > 0 %}{{ cart.itemCount }}{% else %}0{% endif %}</span>
192
+ </button>
193
+ {% endif %}
194
+ </div>
195
+ </div>
196
+
197
+ {% comment %}Mobile Search Row{% endcomment %}
198
+ {% if show_search %}
199
+ <div class="site-header__mobile-search-row">
200
+ <div class="site-header__mobile-search">
201
+ <form class="site-header__mobile-search-form" action="/search" method="get" role="search">
202
+ <div class="site-header__mobile-search-input-wrapper">
203
+ <svg class="site-header__mobile-search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
204
+ <circle cx="11" cy="11" r="8"></circle>
205
+ <path d="m21 21-4.35-4.35"></path>
206
+ </svg>
207
+ <input
208
+ type="search"
209
+ name="q"
210
+ class="site-header__mobile-search-input"
211
+ placeholder="{{ widget_settings.search_placeholder | default: 'Search for...' }}"
212
+ aria-label="Search products"
213
+ autocomplete="off"
214
+ data-mobile-search-input>
215
+ </div>
216
+ </form>
217
+ <div class="site-header__mobile-search-dropdown" data-mobile-search-dropdown style="display: none;">
218
+ <div class="site-header__mobile-search-loading" data-mobile-search-loading style="display: none;">
219
+ <div class="site-header__mobile-search-spinner"></div>
220
+ <span>Searching...</span>
221
+ </div>
222
+ <div class="site-header__mobile-search-results" data-mobile-search-results></div>
223
+ <div class="site-header__mobile-search-empty" data-mobile-search-empty style="display: none;">
224
+ <p>No products found</p>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ {% endif %}
230
+
231
+ {% comment %}Bottom Row: Navigation Menu{% endcomment %}
232
+ <div class="site-header__bottom-row">
233
+ {% if header_menu_widget %}
234
+ <div class="theme-widget-wrapper theme-widget-wrapper--header-menu"
235
+ data-widget-id="{{ header_menu_widget.id }}"
236
+ data-widget-type="{{ header_menu_widget.type }}">
237
+ {% if header_menu_widget and header_menu_widget.template_path %}
238
+ {% render header_menu_widget.template_path, widget: header_menu_widget, settings: settings %}
239
+ {% else %}
240
+ <div class="widget-error">
241
+ <p>Widget template not found</p>
242
+ </div>
243
+ {% endif %}
244
+ </div>
245
+ {% else %}
246
+ {% render 'snippets/mega-menu', %}
247
+ {% endif %}
248
+ </div>
249
+ </div>
250
+ </header>
251
+
252
+ <style>
253
+ :root {
254
+ --header-bg: #ffffff;
255
+ --header-text: #000000;
256
+ --header-border: rgba(0, 0, 0, 0.1);
257
+ --cart-bg: #2c2c2c;
258
+ --cart-text: #ffffff;
259
+ --cart-hover: #1a1a1a;
260
+ --breakpoint-mobile: 768px;
261
+ }
262
+
263
+ /* Header Container */
264
+ .site-header {
265
+ background: var(--header-bg);
266
+ border-bottom: 1px solid var(--header-border);
267
+ position: sticky;
268
+ top: 0;
269
+ z-index: 1030;
270
+ width: 100%;
271
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
272
+ }
273
+
274
+ .site-header__container {
275
+ max-width: {{ settings.container_width | default: 1200 }}px;
276
+ margin: 0 auto;
277
+ padding: 0 20px;
278
+ }
279
+
280
+ /* Top Row Layout */
281
+ .site-header__top-row {
282
+ display: grid;
283
+ grid-template-columns: auto 1fr auto;
284
+ align-items: center;
285
+ gap: 20px;
286
+ padding: 16px 0;
287
+ min-height: 70px;
288
+ }
289
+
290
+ /* Hamburger Menu - Hidden on desktop, visible on mobile */
291
+ .site-header__toggle {
292
+ display: none;
293
+ flex-direction: column;
294
+ justify-content: space-between;
295
+ width: 24px;
296
+ height: 20px;
297
+ border: none;
298
+ background: transparent;
299
+ cursor: pointer;
300
+ padding: 0;
301
+ grid-column: 1;
302
+ }
303
+
304
+ .site-header__toggle span {
305
+ display: block;
306
+ width: 100%;
307
+ height: 2px;
308
+ background-color: var(--header-text);
309
+ transition: all 0.3s ease;
310
+ border-radius: 2px;
311
+ }
312
+
313
+ .site-header__toggle.is-open span:nth-child(1) {
314
+ transform: rotate(45deg) translate(6px, 6px);
315
+ }
316
+
317
+ .site-header__toggle.is-open span:nth-child(2) {
318
+ opacity: 0;
319
+ }
320
+
321
+ .site-header__toggle.is-open span:nth-child(3) {
322
+ transform: rotate(-45deg) translate(7px, -7px);
323
+ }
324
+
325
+ /* Logo */
326
+ .site-header__logo {
327
+ grid-column: 1;
328
+ display: flex;
329
+ align-items: center;
330
+ }
331
+
332
+ .site-header__logo a {
333
+ display: flex;
334
+ align-items: center;
335
+ text-decoration: none;
336
+ }
337
+
338
+ .site-header__logo-image {
339
+ max-height: 40px;
340
+ width: auto;
341
+ display: block;
342
+ }
343
+
344
+ .site-header__brand {
345
+ font-size: 24px;
346
+ font-weight: 700;
347
+ color: var(--header-text);
348
+ }
349
+
350
+ /* Search Wrapper - Desktop */
351
+ .site-header__search-wrapper {
352
+ grid-column: 2;
353
+ display: flex;
354
+ justify-content: center;
355
+ width: 100%;
356
+ max-width: 600px;
357
+ margin: 0 auto;
358
+ }
359
+
360
+ .site-header__search-container {
361
+ width: 100%;
362
+ position: relative;
363
+ }
364
+
365
+ .site-header__search-form {
366
+ width: 100%;
367
+ }
368
+
369
+ .site-header__search-input-wrapper {
370
+ position: relative;
371
+ display: flex;
372
+ align-items: center;
373
+ }
374
+
375
+ .site-header__search-icon {
376
+ position: absolute;
377
+ left: 16px;
378
+ width: 20px;
379
+ height: 20px;
380
+ color: rgba(0, 0, 0, 0.5);
381
+ pointer-events: none;
382
+ }
383
+
384
+ .site-header__search-input {
385
+ width: 100%;
386
+ height: 48px;
387
+ padding: 0 50px 0 48px;
388
+ border: 1px solid rgba(0, 0, 0, 0.15);
389
+ border-radius: 24px;
390
+ font-size: 15px;
391
+ background: var(--header-bg);
392
+ transition: all 0.2s ease;
393
+ }
394
+
395
+ .site-header__search-input:focus {
396
+ outline: none;
397
+ border-color: var(--header-text);
398
+ box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.05);
399
+ }
400
+
401
+ .site-header__search-clear {
402
+ position: absolute;
403
+ right: 12px;
404
+ width: 32px;
405
+ height: 32px;
406
+ display: flex;
407
+ align-items: center;
408
+ justify-content: center;
409
+ border: none;
410
+ background: transparent;
411
+ cursor: pointer;
412
+ border-radius: 50%;
413
+ transition: background 0.2s ease;
414
+ }
415
+
416
+ .site-header__search-clear:hover {
417
+ background: rgba(0, 0, 0, 0.05);
418
+ }
419
+
420
+ .site-header__search-clear-icon {
421
+ width: 16px;
422
+ height: 16px;
423
+ }
424
+
425
+ /* Search Dropdown */
426
+ .site-header__search-dropdown {
427
+ position: absolute;
428
+ top: calc(100% + 8px);
429
+ left: 0;
430
+ right: 0;
431
+ background: white;
432
+ border-radius: 12px;
433
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
434
+ max-height: 400px;
435
+ overflow-y: auto;
436
+ z-index: 1000;
437
+ }
438
+
439
+ .site-header__search-loading {
440
+ display: flex;
441
+ align-items: center;
442
+ justify-content: center;
443
+ gap: 12px;
444
+ padding: 24px;
445
+ }
446
+
447
+ .site-header__search-spinner {
448
+ width: 20px;
449
+ height: 20px;
450
+ border: 2px solid rgba(0, 0, 0, 0.1);
451
+ border-top-color: var(--header-text);
452
+ border-radius: 50%;
453
+ animation: spin 0.6s linear infinite;
454
+ }
455
+
456
+ @keyframes spin {
457
+ to { transform: rotate(360deg); }
458
+ }
459
+
460
+ .site-header__search-results {
461
+ padding: 8px 0;
462
+ }
463
+
464
+ .site-header__search-result {
465
+ display: flex;
466
+ align-items: center;
467
+ gap: 12px;
468
+ padding: 12px 16px;
469
+ text-decoration: none;
470
+ color: var(--header-text);
471
+ transition: background 0.2s ease;
472
+ }
473
+
474
+ .site-header__search-result:hover {
475
+ background: rgba(0, 0, 0, 0.04);
476
+ }
477
+
478
+ .site-header__search-result-image {
479
+ width: 60px;
480
+ height: 60px;
481
+ object-fit: cover;
482
+ border-radius: 8px;
483
+ background: rgba(0, 0, 0, 0.05);
484
+ }
485
+
486
+ .site-header__search-result-info {
487
+ flex: 1;
488
+ }
489
+
490
+ .site-header__search-result-name {
491
+ font-weight: 600;
492
+ font-size: 14px;
493
+ margin-bottom: 4px;
494
+ }
495
+
496
+ .site-header__search-result-price {
497
+ font-size: 14px;
498
+ color: rgba(0, 0, 0, 0.6);
499
+ }
500
+
501
+ .site-header__search-empty {
502
+ padding: 24px;
503
+ text-align: center;
504
+ color: rgba(0, 0, 0, 0.5);
505
+ }
506
+
507
+ /* Actions */
508
+ .site-header__actions {
509
+ grid-column: 3;
510
+ display: flex;
511
+ align-items: center;
512
+ gap: 12px;
513
+ }
514
+
515
+ .site-header__action {
516
+ display: inline-flex;
517
+ align-items: center;
518
+ gap: 8px;
519
+ padding: 10px 16px;
520
+ border: none;
521
+ background: transparent;
522
+ color: var(--header-text);
523
+ text-decoration: none;
524
+ cursor: pointer;
525
+ border-radius: 12px;
526
+ transition: all 0.2s ease;
527
+ white-space: nowrap;
528
+ }
529
+
530
+ .site-header__action:hover {
531
+ background: rgba(0, 0, 0, 0.04);
532
+ }
533
+
534
+ .site-header__action--account {
535
+ border: 1px solid var(--header-border);
536
+ }
537
+
538
+ .site-header__action--cart {
539
+ background: var(--cart-bg);
540
+ color: var(--cart-text);
541
+ padding: 10px 20px;
542
+ border-radius: 24px;
543
+ position: relative;
544
+ }
545
+
546
+ .site-header__action--cart:hover {
547
+ background: var(--cart-hover);
548
+ }
549
+
550
+ .site-header__account-icon,
551
+ .site-header__login-icon,
552
+ .site-header__logout-icon,
553
+ .site-header__cart-icon {
554
+ width: 20px;
555
+ height: 20px;
556
+ }
557
+
558
+ .site-header__cart-text {
559
+ display: none;
560
+ }
561
+
562
+ .site-header__cart-count {
563
+ position: absolute;
564
+ top: -4px;
565
+ right: -4px;
566
+ min-width: 20px;
567
+ height: 20px;
568
+ padding: 0 6px;
569
+ background: #ff4444;
570
+ color: white;
571
+ border-radius: 10px;
572
+ font-size: 11px;
573
+ font-weight: 600;
574
+ display: flex;
575
+ align-items: center;
576
+ justify-content: center;
577
+ }
578
+
579
+ .site-header__logout-form {
580
+ margin: 0;
581
+ }
582
+
583
+ /* Mobile Search Row - Hidden on desktop */
584
+ .site-header__mobile-search-row {
585
+ display: none;
586
+ padding: 12px 0;
587
+ border-top: 1px solid var(--header-border);
588
+ }
589
+
590
+ .site-header__mobile-search {
591
+ position: relative;
592
+ }
593
+
594
+ .site-header__mobile-search-input-wrapper {
595
+ position: relative;
596
+ }
597
+
598
+ .site-header__mobile-search-icon {
599
+ position: absolute;
600
+ left: 16px;
601
+ top: 50%;
602
+ transform: translateY(-50%);
603
+ width: 20px;
604
+ height: 20px;
605
+ color: rgba(0, 0, 0, 0.5);
606
+ pointer-events: none;
607
+ }
608
+
609
+ .site-header__mobile-search-input {
610
+ width: 100%;
611
+ height: 44px;
612
+ padding: 0 16px 0 48px;
613
+ border: 1px solid rgba(0, 0, 0, 0.15);
614
+ border-radius: 22px;
615
+ font-size: 15px;
616
+ background: var(--header-bg);
617
+ }
618
+
619
+ .site-header__mobile-search-input:focus {
620
+ outline: none;
621
+ border-color: var(--header-text);
622
+ }
623
+
624
+ .site-header__mobile-search-dropdown {
625
+ position: absolute;
626
+ top: calc(100% + 8px);
627
+ left: 0;
628
+ right: 0;
629
+ background: white;
630
+ border-radius: 12px;
631
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
632
+ max-height: 400px;
633
+ overflow-y: auto;
634
+ z-index: 1000;
635
+ }
636
+
637
+ .site-header__mobile-search-loading {
638
+ display: flex;
639
+ align-items: center;
640
+ justify-content: center;
641
+ gap: 12px;
642
+ padding: 24px;
643
+ }
644
+
645
+ .site-header__mobile-search-spinner {
646
+ width: 20px;
647
+ height: 20px;
648
+ border: 2px solid rgba(0, 0, 0, 0.1);
649
+ border-top-color: var(--header-text);
650
+ border-radius: 50%;
651
+ animation: spin 0.6s linear infinite;
652
+ }
653
+
654
+ .site-header__mobile-search-results {
655
+ padding: 8px 0;
656
+ }
657
+
658
+ .site-header__mobile-search-result {
659
+ display: flex;
660
+ align-items: center;
661
+ gap: 12px;
662
+ padding: 12px 16px;
663
+ text-decoration: none;
664
+ color: var(--header-text);
665
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
666
+ }
667
+
668
+ .site-header__mobile-search-result:hover {
669
+ background: rgba(0, 0, 0, 0.04);
670
+ }
671
+
672
+ .site-header__mobile-search-result-image {
673
+ width: 60px;
674
+ height: 60px;
675
+ object-fit: cover;
676
+ border-radius: 8px;
677
+ background: rgba(0, 0, 0, 0.05);
678
+ }
679
+
680
+ .site-header__mobile-search-result-info {
681
+ flex: 1;
682
+ }
683
+
684
+ .site-header__mobile-search-result-name {
685
+ font-weight: 600;
686
+ font-size: 14px;
687
+ margin-bottom: 4px;
688
+ }
689
+
690
+ .site-header__mobile-search-result-price {
691
+ font-size: 14px;
692
+ color: rgba(0, 0, 0, 0.6);
693
+ }
694
+
695
+ .site-header__mobile-search-empty {
696
+ padding: 24px;
697
+ text-align: center;
698
+ color: rgba(0, 0, 0, 0.5);
699
+ }
700
+
701
+ /* Bottom Row */
702
+ .site-header__bottom-row {
703
+ border-top: 1px solid var(--header-border);
704
+ padding: 12px 0;
705
+ }
706
+
707
+ /* Mobile Responsive */
708
+ @media (max-width: 768px) {
709
+ .site-header__container {
710
+ padding: 0 16px;
711
+ }
712
+
713
+ .site-header__top-row {
714
+ grid-template-columns: auto 1fr auto;
715
+ gap: 12px;
716
+ padding: 12px 0;
717
+ min-height: 60px;
718
+ }
719
+
720
+ /* Show hamburger on mobile */
721
+ .site-header__toggle {
722
+ display: flex !important;
723
+ }
724
+
725
+ /* Logo moves to center on mobile */
726
+ .site-header__logo {
727
+ grid-column: 2;
728
+ justify-content: center;
729
+ }
730
+
731
+ .site-header__logo-image {
732
+ max-height: 32px;
733
+ }
734
+
735
+ .site-header__brand {
736
+ font-size: 18px;
737
+ }
738
+
739
+ /* Hide desktop search on mobile */
740
+ .site-header__search-wrapper {
741
+ display: none !important;
742
+ }
743
+
744
+ /* Show mobile search */
745
+ .site-header__mobile-search-row {
746
+ display: block;
747
+ }
748
+
749
+ /* Actions on mobile */
750
+ .site-header__actions {
751
+ grid-column: 3;
752
+ gap: 8px;
753
+ }
754
+
755
+ /* Hide cart text on mobile, show icon only */
756
+ .site-header__cart-text {
757
+ display: none;
758
+ }
759
+
760
+ .site-header__action--cart {
761
+ padding: 10px;
762
+ min-width: 44px;
763
+ justify-content: center;
764
+ }
765
+
766
+ /* Reduce action padding on mobile */
767
+ .site-header__action {
768
+ padding: 10px;
769
+ }
770
+
771
+ .site-header__action--account {
772
+ padding: 8px;
773
+ }
774
+
775
+ /* Hide bottom row (navigation) on mobile */
776
+ .site-header__bottom-row {
777
+ display: none;
778
+ }
779
+ }
780
+
781
+ /* Tablet adjustments */
782
+ @media (min-width: 769px) and (max-width: 1024px) {
783
+ .site-header__search-wrapper {
784
+ max-width: 400px;
785
+ }
786
+
787
+ .site-header__cart-text {
788
+ display: none;
789
+ }
790
+ }
791
+ </style>
792
+
793
+ <script>
794
+ document.addEventListener('DOMContentLoaded', function() {
795
+ // Desktop Search
796
+ const searchContainer = document.querySelector('[data-search-container]');
797
+ if (searchContainer) {
798
+ const searchInput = searchContainer.querySelector('[data-search-input]');
799
+ const searchClear = searchContainer.querySelector('[data-search-clear]');
800
+ const searchDropdown = searchContainer.querySelector('[data-search-dropdown]');
801
+ const searchResults = searchContainer.querySelector('[data-search-results]');
802
+ const searchLoading = searchContainer.querySelector('[data-search-loading]');
803
+ const searchEmpty = searchContainer.querySelector('[data-search-empty]');
804
+
805
+ let searchTimeout;
806
+
807
+ if (searchInput) {
808
+ searchInput.addEventListener('input', function() {
809
+ const query = this.value.trim();
810
+
811
+ if (searchClear) {
812
+ searchClear.style.display = query ? 'flex' : 'none';
813
+ }
814
+
815
+ if (query.length >= 2) {
816
+ clearTimeout(searchTimeout);
817
+ searchTimeout = setTimeout(() => performSearch(query), 300);
818
+ } else {
819
+ searchDropdown.style.display = 'none';
820
+ }
821
+ });
822
+ }
823
+
824
+ if (searchClear) {
825
+ searchClear.addEventListener('click', function() {
826
+ searchInput.value = '';
827
+ searchClear.style.display = 'none';
828
+ searchDropdown.style.display = 'none';
829
+ searchInput.focus();
830
+ });
831
+ }
832
+
833
+ async function performSearch(query) {
834
+ searchLoading.style.display = 'flex';
835
+ searchResults.innerHTML = '';
836
+ searchEmpty.style.display = 'none';
837
+ searchDropdown.style.display = 'block';
838
+
839
+ try {
840
+ const response = await fetch(`/webstoreapi/search/autocomplete?q=${encodeURIComponent(query)}`);
841
+ const data = await response.json();
842
+
843
+ searchLoading.style.display = 'none';
844
+
845
+ if (data.success && data.products && data.products.length > 0) {
846
+ displayResults(data.products);
847
+ } else {
848
+ searchEmpty.style.display = 'block';
849
+ }
850
+ } catch (error) {
851
+ console.error('Search error:', error);
852
+ searchLoading.style.display = 'none';
853
+ searchEmpty.style.display = 'block';
854
+ }
855
+ }
856
+
857
+ function displayResults(products) {
858
+ searchResults.innerHTML = '';
859
+ products.forEach(product => {
860
+ const resultItem = document.createElement('a');
861
+ const productSlug = product.slug || product.id;
862
+ resultItem.href = productSlug ? `/${productSlug}` : '#';
863
+ resultItem.className = 'site-header__search-result';
864
+
865
+ const imageHtml = product.thumbnailUrl
866
+ ? `<img src="${product.thumbnailUrl}" alt="${product.name}" class="site-header__search-result-image" loading="lazy">`
867
+ : `<div class="site-header__search-result-image" style="background-color: rgba(0,0,0,0.05); display: flex; align-items: center; justify-content: center; color: rgba(0,0,0,0.3); font-size: 0.75rem;">No image</div>`;
868
+
869
+ resultItem.innerHTML = `
870
+ ${imageHtml}
871
+ <div class="site-header__search-result-info">
872
+ <div class="site-header__search-result-name">${escapeHtml(product.name)}</div>
873
+ <div class="site-header__search-result-price">${product.prices.priceString || 'Price not available'}</div>
874
+ </div>
875
+ `;
876
+
877
+ searchResults.appendChild(resultItem);
878
+ });
879
+ }
880
+
881
+ function escapeHtml(text) {
882
+ const div = document.createElement('div');
883
+ div.textContent = text;
884
+ return div.innerHTML;
885
+ }
886
+
887
+ document.addEventListener('click', function(e) {
888
+ if (searchContainer && !searchContainer.contains(e.target)) {
889
+ searchDropdown.style.display = 'none';
890
+ }
891
+ });
892
+ }
893
+
894
+ // Mobile Search
895
+ const mobileSearch = document.querySelector('.site-header__mobile-search');
896
+ if (mobileSearch) {
897
+ const mobileSearchInput = mobileSearch.querySelector('[data-mobile-search-input]');
898
+ const mobileSearchDropdown = mobileSearch.querySelector('[data-mobile-search-dropdown]');
899
+ const mobileSearchResults = mobileSearch.querySelector('[data-mobile-search-results]');
900
+ const mobileSearchLoading = mobileSearch.querySelector('[data-mobile-search-loading]');
901
+ const mobileSearchEmpty = mobileSearch.querySelector('[data-mobile-search-empty]');
902
+ const mobileSearchForm = mobileSearch.querySelector('.site-header__mobile-search-form');
903
+
904
+ let mobileSearchTimeout;
905
+
906
+ if (mobileSearchInput) {
907
+ mobileSearchInput.addEventListener('input', function() {
908
+ const query = this.value.trim();
909
+
910
+ if (query.length >= 2) {
911
+ clearTimeout(mobileSearchTimeout);
912
+ mobileSearchTimeout = setTimeout(() => performMobileSearch(query), 300);
913
+ } else {
914
+ mobileSearchDropdown.style.display = 'none';
915
+ }
916
+ });
917
+ }
918
+
919
+ if (mobileSearchForm) {
920
+ mobileSearchForm.addEventListener('submit', function(e) {
921
+ const query = mobileSearchInput.value.trim();
922
+ if (!query || query.length < 2) {
923
+ e.preventDefault();
924
+ }
925
+ });
926
+ }
927
+
928
+ async function performMobileSearch(query) {
929
+ mobileSearchLoading.style.display = 'flex';
930
+ mobileSearchResults.innerHTML = '';
931
+ mobileSearchEmpty.style.display = 'none';
932
+ mobileSearchDropdown.style.display = 'block';
933
+
934
+ try {
935
+ const response = await fetch(`/webstoreapi/search/autocomplete?q=${encodeURIComponent(query)}`);
936
+ const data = await response.json();
937
+
938
+ mobileSearchLoading.style.display = 'none';
939
+
940
+ if (data.success && data.products && data.products.length > 0) {
941
+ displayMobileResults(data.products);
942
+ } else {
943
+ mobileSearchEmpty.style.display = 'block';
944
+ }
945
+ } catch (error) {
946
+ console.error('Mobile search error:', error);
947
+ mobileSearchLoading.style.display = 'none';
948
+ mobileSearchEmpty.style.display = 'block';
949
+ }
950
+ }
951
+
952
+ function displayMobileResults(products) {
953
+ mobileSearchResults.innerHTML = '';
954
+ products.forEach(product => {
955
+ const resultItem = document.createElement('a');
956
+ const productSlug = product.slug || product.id;
957
+ resultItem.href = productSlug ? `/${productSlug}` : '#';
958
+ resultItem.className = 'site-header__mobile-search-result';
959
+
960
+ const imageHtml = product.thumbnailUrl
961
+ ? `<img src="${product.thumbnailUrl}" alt="${product.name}" class="site-header__mobile-search-result-image" loading="lazy">`
962
+ : `<div class="site-header__mobile-search-result-image" style="background-color: rgba(0,0,0,0.05); display: flex; align-items: center; justify-content: center; color: rgba(0,0,0,0.3); font-size: 0.75rem;">No image</div>`;
963
+
964
+ resultItem.innerHTML = `
965
+ ${imageHtml}
966
+ <div class="site-header__mobile-search-result-info">
967
+ <div class="site-header__mobile-search-result-name">${escapeHtml(product.name)}</div>
968
+ <div class="site-header__mobile-search-result-price">${product.prices.priceString || 'Price not available'}</div>
969
+ </div>
970
+ `;
971
+
972
+ mobileSearchResults.appendChild(resultItem);
973
+ });
974
+ }
975
+
976
+ function escapeHtml(text) {
977
+ const div = document.createElement('div');
978
+ div.textContent = text;
979
+ return div.innerHTML;
980
+ }
981
+
982
+ document.addEventListener('click', function(e) {
983
+ if (mobileSearch && !mobileSearch.contains(e.target)) {
984
+ mobileSearchDropdown.style.display = 'none';
985
+ }
986
+ });
987
+ }
988
+
989
+ // Check authentication state - only check non-httpOnly cookie
990
+ function isAuthenticated() {
991
+ // O2VENDUserToken is httpOnly, so we check O2VENDIsUserLoggedin flag cookie
992
+ const cookies = document.cookie.split(';');
993
+ for (let cookie of cookies) {
994
+ const [name, value] = cookie.trim().split('=');
995
+ if (name === 'O2VENDIsUserLoggedin' && (value === 'true' || value === '1')) {
996
+ return true;
997
+ }
998
+ }
999
+ return false;
1000
+ }
1001
+
1002
+ // Update UI to show account/logout icons when authenticated
1003
+ function updateAuthUI() {
1004
+ const authenticated = isAuthenticated();
1005
+ const accountIcon = document.querySelector('.site-header__action--account');
1006
+ const logoutForm = document.querySelector('.site-header__logout-form');
1007
+ const loginIcon = document.querySelector('.site-header__action--login');
1008
+
1009
+ if (authenticated) {
1010
+ // User is authenticated - show account and logout icons
1011
+ // Create account icon if it doesn't exist
1012
+ if (!accountIcon && document.querySelector('.site-header__actions')) {
1013
+ const actionsContainer = document.querySelector('.site-header__actions');
1014
+ const accountLink = document.createElement('a');
1015
+ accountLink.href = '/account';
1016
+ accountLink.className = 'site-header__action site-header__action--account';
1017
+ accountLink.setAttribute('aria-label', 'Account');
1018
+ accountLink.innerHTML = '<svg class="site-header__account-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>';
1019
+ actionsContainer.insertBefore(accountLink, actionsContainer.firstChild);
1020
+ }
1021
+
1022
+ // Create logout form if it doesn't exist
1023
+ if (!logoutForm && document.querySelector('.site-header__actions')) {
1024
+ const actionsContainer = document.querySelector('.site-header__actions');
1025
+ const logoutFormEl = document.createElement('form');
1026
+ logoutFormEl.method = 'post';
1027
+ logoutFormEl.action = '/webstoreapi/customer/logout';
1028
+ logoutFormEl.className = 'site-header__logout-form';
1029
+ logoutFormEl.innerHTML = '<button type="submit" class="site-header__action site-header__action--logout" aria-label="Logout"><svg class="site-header__logout-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg></button>';
1030
+ const accountIconEl = document.querySelector('.site-header__action--account');
1031
+ if (accountIconEl) {
1032
+ accountIconEl.parentNode.insertBefore(logoutFormEl, accountIconEl.nextSibling);
1033
+ } else {
1034
+ actionsContainer.appendChild(logoutFormEl);
1035
+ }
1036
+ }
1037
+
1038
+ // Show existing elements if they're hidden
1039
+ const accountIconEl = document.querySelector('.site-header__action--account');
1040
+ const logoutFormEl = document.querySelector('.site-header__logout-form');
1041
+ if (accountIconEl && accountIconEl.offsetParent === null) {
1042
+ accountIconEl.style.display = '';
1043
+ }
1044
+ if (logoutFormEl && logoutFormEl.offsetParent === null) {
1045
+ logoutFormEl.style.display = '';
1046
+ }
1047
+ if (loginIcon && loginIcon.offsetParent !== null) {
1048
+ loginIcon.style.display = 'none';
1049
+ }
1050
+ } else {
1051
+ // User is not authenticated - hide account and logout icons
1052
+ if (accountIcon && accountIcon.offsetParent !== null) {
1053
+ accountIcon.style.display = 'none';
1054
+ }
1055
+ if (logoutForm && logoutForm.offsetParent !== null) {
1056
+ logoutForm.style.display = 'none';
1057
+ }
1058
+ if (loginIcon && loginIcon.offsetParent === null) {
1059
+ loginIcon.style.display = '';
1060
+ }
1061
+ }
1062
+ }
1063
+
1064
+ // Run check after a short delay to ensure DOM is ready
1065
+ setTimeout(updateAuthUI, 100);
1066
+
1067
+ // Also listen for login success events and update UI immediately
1068
+ // This handles cases where login happens without page refresh
1069
+ document.addEventListener('loginSuccess', function() {
1070
+ setTimeout(updateAuthUI, 200);
1071
+ });
1072
+
1073
+ // Monitor for login modal success view being shown
1074
+ const observer = new MutationObserver(function(mutations) {
1075
+ const successView = document.querySelector('[data-login-view="success"]');
1076
+ if (successView && !successView.hidden) {
1077
+ // Login success detected - update UI
1078
+ setTimeout(updateAuthUI, 300);
1079
+ }
1080
+ });
1081
+
1082
+ const loginModal = document.getElementById('login-modal');
1083
+ if (loginModal) {
1084
+ observer.observe(loginModal, {
1085
+ attributes: true,
1086
+ attributeFilter: ['hidden'],
1087
+ childList: true,
1088
+ subtree: true
1089
+ });
1090
+ }
1091
+
1092
+ // Also check periodically (every 2 seconds) if user becomes authenticated
1093
+ // This handles cases where cookies are set but UI hasn't updated
1094
+ setInterval(function() {
1095
+ const authenticated = isAuthenticated();
1096
+ const accountIcon = document.querySelector('.site-header__action--account');
1097
+ const logoutForm = document.querySelector('.site-header__logout-form');
1098
+
1099
+ // Only update if we detect a mismatch
1100
+ if (authenticated && (!accountIcon || accountIcon.offsetParent === null || !logoutForm || logoutForm.offsetParent === null)) {
1101
+ updateAuthUI();
1102
+ }
1103
+ }, 2000);
1104
+
1105
+ // Handle account icon click - check authentication and open login modal if needed
1106
+ const accountIcon = document.querySelector('.site-header__action--account');
1107
+ if (accountIcon) {
1108
+ accountIcon.addEventListener('click', function(e) {
1109
+ // If not authenticated, prevent navigation and open login modal
1110
+ if (!isAuthenticated()) {
1111
+ e.preventDefault();
1112
+ e.stopPropagation();
1113
+
1114
+ // Try to open login modal
1115
+ const loginTrigger = document.querySelector('[data-login-modal-trigger]');
1116
+ if (loginTrigger) {
1117
+ loginTrigger.click();
1118
+ } else if (window.Theme && typeof window.Theme.openLoginModal === 'function') {
1119
+ window.Theme.openLoginModal();
1120
+ } else {
1121
+ // Fallback: redirect to login page
1122
+ window.location.href = '/customers/login';
1123
+ }
1124
+ }
1125
+ });
1126
+ }
1127
+ });
1128
+ </script>
1129
+
1130
+ {% schema %}
1131
+ {
1132
+ "name": "Header",
1133
+ "settings": [
1134
+ {
1135
+ "type": "paragraph",
1136
+ "content": "This section displays widgets from the 'header' section. Widgets are automatically ordered by their Position field and can be reordered in the visual editor."
1137
+ }
1138
+ ],
1139
+ "presets": [
1140
+ {
1141
+ "name": "Header"
1142
+ }
1143
+ ]
1144
+ }
1145
+ {% endschema %}