@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,340 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_data = widget.data
4
+
5
+ comment
6
+ Dynamic Widget: Categories come from widget_data.categories (enriched by WidgetService)
7
+ Settings come from widget_settings
8
+ endcomment
9
+ assign categories = widget_data.categories | default: widget_data.Categories
10
+ assign columns = widget_settings.columns | default: 4
11
+ assign title = widget_settings.title
12
+ assign subtitle = widget_settings.subtitle
13
+ assign show_widget_title_raw = widget_settings.showWidgetTitle | default: 'Yes'
14
+ if show_widget_title_raw == null or show_widget_title_raw == blank or show_widget_title_raw == 'null'
15
+ assign show_widget_title = true
16
+ else
17
+ if show_widget_title_raw == 'Yes'
18
+ assign show_widget_title = true
19
+ elsif show_widget_title_raw == true
20
+ assign show_widget_title = true
21
+ else
22
+ assign show_widget_title = false
23
+ endif
24
+ endif
25
+ assign widget_title_alignment_raw = widget_settings.widgetTitleAlignment | default: 'center'
26
+ assign widget_title_alignment = widget_title_alignment_raw | downcase
27
+ if widget_title_alignment != 'left' and widget_title_alignment != 'right' and widget_title_alignment != 'center'
28
+ assign widget_title_alignment = 'center'
29
+ endif
30
+ assign show_counts = widget_settings.showProductCount | default: false
31
+ assign background_color = widget_settings.backgroundColor
32
+ assign text_color = widget_settings.textColor
33
+ %}
34
+
35
+ <section class="widget widget-category-list" data-widget-id="{{ widget.id }}"{% if background_color and background_color != 'null' %} style="background-color: {{ background_color }};"{% endif %}>
36
+ <div class="widget-category-list__inner">
37
+ {% if show_widget_title %}
38
+ {% if title or subtitle %}
39
+ <header class="widget-header" style="text-align: {{ widget_title_alignment }};{% if text_color and text_color != 'null' %} color: {{ text_color }};{% endif %}">
40
+ {% if title %}
41
+ <h2 class="widget-title">{{ title }}</h2>
42
+ {% endif %}
43
+ {% if subtitle %}
44
+ <p class="widget-subtitle">{{ subtitle }}</p>
45
+ {% endif %}
46
+ </header>
47
+ {% endif %}
48
+ {% endif %}
49
+
50
+ {% if categories and categories.size > 0 %}
51
+ <div class="widget-category-list__grid widget-category-list__grid--{{ columns }}-col">
52
+ {% for category in categories %}
53
+ {% comment %} Extract category data with fallbacks for different API formats {% endcomment %}
54
+ {% assign cat_name = category.name | default: category.Name | default: category.title | default: category.Title %}
55
+ {% assign cat_slug = category.slug | default: category.Slug | default: category.handle | default: category.Handle %}
56
+ {% assign cat_id = category.id | default: category.Id | default: category.categoryId | default: category.CategoryId %}
57
+ {% assign cat_description = category.description | default: category.Description %}
58
+ {% assign cat_product_count = category.productCount | default: category.ProductCount %}
59
+
60
+ {% comment %} Build category URL - slug is already a complete URL {% endcomment %}
61
+ {% assign cat_url = category.url | default: category.Url | default: category.link | default: category.Link %}
62
+ {% if cat_url == blank %}
63
+ {% if cat_slug and cat_slug != blank %}
64
+ {% assign cat_url = cat_slug %}
65
+ {% else %}
66
+ {% assign cat_url = '#' %}
67
+ {% endif %}
68
+ {% endif %}
69
+
70
+ {% comment %} Get category image with fallbacks {% endcomment %}
71
+ {% assign cat_image = nil %}
72
+ {% if category.thumbnailImage and category.thumbnailImage.url %}
73
+ {% assign cat_image = category.thumbnailImage.url %}
74
+ {% elsif category.ThumbnailImage and category.ThumbnailImage.Url %}
75
+ {% assign cat_image = category.ThumbnailImage.Url %}
76
+ {% elsif category.bannerImage and category.bannerImage.url %}
77
+ {% assign cat_image = category.bannerImage.url %}
78
+ {% elsif category.BannerImage and category.BannerImage.Url %}
79
+ {% assign cat_image = category.BannerImage.Url %}
80
+ {% elsif category.thumbnailImage and category.thumbnailImage != blank %}
81
+ {% assign cat_image = category.thumbnailImage %}
82
+ {% elsif category.ThumbnailImage and category.ThumbnailImage != blank %}
83
+ {% assign cat_image = category.ThumbnailImage %}
84
+ {% elsif category.bannerImage and category.bannerImage != blank %}
85
+ {% assign cat_image = category.bannerImage %}
86
+ {% elsif category.BannerImage and category.BannerImage != blank %}
87
+ {% assign cat_image = category.BannerImage %}
88
+ {% elsif category.image %}
89
+ {% assign cat_image = category.image %}
90
+ {% elsif category.Image %}
91
+ {% assign cat_image = category.Image %}
92
+ {% endif %}
93
+
94
+ <article class="widget-category-list__item">
95
+ <a href="{{ cat_url }}" class="widget-category-list__link">
96
+ <div class="widget-category-list__media">
97
+ {% if cat_image != blank and cat_image != nil and cat_image != 'null' %}
98
+ <img src="{{ cat_image }}" alt="{{ cat_name }}" width="300" height="300" loading="lazy" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
99
+ <span class="widget-category-list__placeholder widget-category-list__placeholder--fallback" style="display: none;" aria-hidden="true">
100
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
101
+ <path d="M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z"/>
102
+ </svg>
103
+ </span>
104
+ {% else %}
105
+ <span class="widget-category-list__placeholder" aria-hidden="true">
106
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
107
+ <path d="M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z"/>
108
+ </svg>
109
+ </span>
110
+ {% endif %}
111
+ </div>
112
+ <div class="widget-category-list__content">
113
+ <h3>{{ cat_name }}</h3>
114
+ {% if show_counts and cat_product_count %}
115
+ <p class="widget-category-list__count">{{ cat_product_count }} {{ 'products' | t }}</p>
116
+ {% elsif cat_description and cat_description != blank %}
117
+ <p class="widget-category-list__description">{{ cat_description | truncate: 60 }}</p>
118
+ {% endif %}
119
+ </div>
120
+ </a>
121
+ </article>
122
+ {% endfor %}
123
+ </div>
124
+ {% else %}
125
+ <div class="widget-empty">
126
+ <p>{{ widget_settings.empty_state | default: 'Categories will appear here soon.' }}</p>
127
+ </div>
128
+ {% endif %}
129
+ </div>
130
+
131
+ <style>
132
+ .widget-category-list {
133
+ padding: 48px 0;
134
+ background: transparent;
135
+ position: relative;
136
+ }
137
+
138
+ .widget-category-list__inner {
139
+ max-width: 1400px;
140
+ margin: 0 auto;
141
+ padding: 0 24px;
142
+ }
143
+
144
+ .widget-category-list .widget-header {
145
+ text-align: center;
146
+ margin-bottom: 32px;
147
+ position: static;
148
+ z-index: auto;
149
+ }
150
+
151
+ .widget-category-list .widget-title {
152
+ font-size: 1.4rem;
153
+ font-weight: 500;
154
+ line-height: 1.3;
155
+ color: #111;
156
+ margin: 0 0 8px 0;
157
+ letter-spacing: -0.01em;
158
+ }
159
+
160
+ .widget-category-list .widget-subtitle {
161
+ font-size: 14px;
162
+ color: #6b7280;
163
+ margin: 0;
164
+ }
165
+
166
+ .widget-category-list__grid {
167
+ display: grid;
168
+ gap: 24px;
169
+ }
170
+
171
+ .widget-category-list__grid--2-col { grid-template-columns: repeat(2, 1fr); }
172
+ .widget-category-list__grid--3-col { grid-template-columns: repeat(3, 1fr); }
173
+ .widget-category-list__grid--4-col { grid-template-columns: repeat(4, 1fr); }
174
+ .widget-category-list__grid--5-col { grid-template-columns: repeat(5, 1fr); }
175
+ .widget-category-list__grid--6-col { grid-template-columns: repeat(6, 1fr); }
176
+
177
+ .widget-category-list__item {
178
+ background: #fff;
179
+ border-radius: 12px;
180
+ overflow: hidden;
181
+ box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);
182
+ transition: transform 0.25s ease, box-shadow 0.25s ease;
183
+ }
184
+
185
+ .widget-category-list__item:hover {
186
+ transform: translateY(-4px);
187
+ box-shadow: 0 12px 24px rgba(15, 23, 42, 0.12);
188
+ }
189
+
190
+ .widget-category-list__link {
191
+ display: block;
192
+ text-decoration: none;
193
+ color: inherit;
194
+ }
195
+
196
+ .widget-category-list__media {
197
+ aspect-ratio: 4 / 3;
198
+ overflow: hidden;
199
+ position: relative;
200
+ background: #f5f5f5;
201
+ }
202
+
203
+ .widget-category-list__media img {
204
+ width: 100%;
205
+ height: 100%;
206
+ object-fit: cover;
207
+ transition: transform 0.3s ease;
208
+ }
209
+
210
+ .widget-category-list__item:hover .widget-category-list__media img {
211
+ transform: scale(1.05);
212
+ }
213
+
214
+ .widget-category-list__placeholder {
215
+ width: 100%;
216
+ height: 100%;
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: center;
220
+ background: linear-gradient(135deg, #f0f0f0 0%, #e8e8e8 100%);
221
+ color: #9ca3af;
222
+ }
223
+
224
+ .widget-category-list__placeholder--fallback {
225
+ position: absolute;
226
+ top: 0;
227
+ left: 0;
228
+ }
229
+
230
+ .widget-category-list__content {
231
+ padding: 16px;
232
+ }
233
+
234
+ .widget-category-list__content h3 {
235
+ margin: 0;
236
+ font-size: 16px;
237
+ font-weight: 600;
238
+ color: #111;
239
+ line-height: 1.3;
240
+ }
241
+
242
+ .widget-category-list__count {
243
+ margin: 6px 0 0;
244
+ color: #6b7280;
245
+ font-size: 13px;
246
+ }
247
+
248
+ .widget-category-list__description {
249
+ margin: 6px 0 0;
250
+ color: #6b7280;
251
+ font-size: 13px;
252
+ line-height: 1.4;
253
+ }
254
+
255
+ .widget-category-list .widget-empty {
256
+ text-align: center;
257
+ padding: 60px 24px;
258
+ color: #6b7280;
259
+ }
260
+
261
+ /* Tablet: 3 columns for 4+ col layouts */
262
+ @media screen and (max-width: 1024px) {
263
+ .widget-category-list__grid--4-col,
264
+ .widget-category-list__grid--5-col,
265
+ .widget-category-list__grid--6-col {
266
+ grid-template-columns: repeat(3, 1fr);
267
+ }
268
+ .widget-category-list__grid {
269
+ gap: 20px;
270
+ }
271
+ }
272
+
273
+ /* Small Tablet: 2 columns */
274
+ @media screen and (max-width: 768px) {
275
+ .widget-category-list {
276
+ padding: 36px 0;
277
+ }
278
+ .widget-category-list__inner {
279
+ padding: 0 16px;
280
+ }
281
+ .widget-category-list .widget-header {
282
+ margin-bottom: 24px;
283
+ }
284
+ .widget-category-list .widget-title {
285
+ font-size: 1.3rem;
286
+ }
287
+ .widget-category-list__grid,
288
+ .widget-category-list__grid--3-col,
289
+ .widget-category-list__grid--4-col,
290
+ .widget-category-list__grid--5-col,
291
+ .widget-category-list__grid--6-col {
292
+ grid-template-columns: repeat(2, 1fr);
293
+ gap: 16px;
294
+ }
295
+ .widget-category-list__content {
296
+ padding: 12px;
297
+ }
298
+ .widget-category-list__content h3 {
299
+ font-size: 14px;
300
+ }
301
+ }
302
+
303
+ /* Mobile: 2 columns with smaller cards */
304
+ @media screen and (max-width: 480px) {
305
+ .widget-category-list__inner {
306
+ padding: 0 12px;
307
+ }
308
+ .widget-category-list__grid {
309
+ gap: 12px;
310
+ }
311
+ .widget-category-list__item {
312
+ border-radius: 8px;
313
+ }
314
+ .widget-category-list__content {
315
+ padding: 10px;
316
+ }
317
+ .widget-category-list__content h3 {
318
+ font-size: 13px;
319
+ }
320
+ .widget-category-list__count,
321
+ .widget-category-list__description {
322
+ font-size: 12px;
323
+ }
324
+ .widget-category-list .widget-title {
325
+ font-size: 1.2rem;
326
+ }
327
+ }
328
+
329
+ /* Extra small: still 2 columns */
330
+ @media screen and (max-width: 360px) {
331
+ .widget-category-list__grid {
332
+ gap: 8px;
333
+ }
334
+ .widget-category-list__content {
335
+ padding: 8px;
336
+ }
337
+ }
338
+ </style>
339
+ </section>
340
+