@o2vend/theme-cli 1.0.35 → 1.0.36

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.
@@ -10,29 +10,41 @@
10
10
  function generateMockData() {
11
11
  return {
12
12
  store: {
13
- id: 'mock-store-1',
13
+ id: 1,
14
+ identifier: 'mock-store',
14
15
  name: 'My O2VEND Store',
15
- description: 'A beautiful e-commerce store powered by O2VEND',
16
16
  domain: 'localhost:3000',
17
- email: 'store@example.com',
18
- phone: '+1234567890',
19
- address: {
20
- address1: '123 Main Street',
21
- city: 'New York',
22
- state: 'NY',
23
- zip: '10001',
24
- country: 'US'
25
- },
17
+ companyName: 'My O2VEND Store',
18
+ companyAddress: '123 Main Street, New York, NY 10001',
19
+ companyPhoneNumber: '+1234567890',
20
+ companyEmail: 'store@example.com',
26
21
  currency: 'USD',
27
22
  currencySymbol: '$',
28
- locale: 'en-US',
29
23
  timezone: 'America/New_York',
24
+ language: 'en',
25
+ logoUrl: '/assets/logo.png',
26
+ favouriteIconUrl: '/favicon.ico',
30
27
  settings: {
28
+ currency: 'USD',
31
29
  currencySymbol: '$',
32
30
  currencyFormat: '#,##0.00',
33
31
  currencyDecimalDigits: 2,
34
32
  currencyGroupSeparator: ',',
35
- currencyDecimalSeparator: '.'
33
+ currencyDecimalSeparator: '.',
34
+ currencyGroupSizes: [3],
35
+ deliveryZoneSupport: false,
36
+ deliveryZoneSelection: 0,
37
+ defaultDeliveryZoneZipCode: null,
38
+ loginTypes: null,
39
+ zipcodeSearchEnabled: false,
40
+ referEnabled: false,
41
+ shoppingCartIconType: null,
42
+ timezone: 'America/New_York',
43
+ language: 'en',
44
+ logo: '/assets/logo.png',
45
+ favicon: '/favicon.ico',
46
+ countries: [],
47
+ states: {}
36
48
  }
37
49
  },
38
50
 
@@ -50,8 +62,10 @@ function generateMockData() {
50
62
  id: 'mock-cart-1',
51
63
  items: [],
52
64
  total: 0,
53
- itemCount: 0,
54
- currency: 'USD'
65
+ subTotal: 0,
66
+ taxAmount: 0,
67
+ shippingAmount: 0,
68
+ itemCount: 0
55
69
  }
56
70
  };
57
71
  }
@@ -67,21 +81,21 @@ function generateMockProducts(count = 20) {
67
81
 
68
82
  // Category mapping - map category names to category IDs (matching generateMockCategories order)
69
83
  const categoryMap = {
70
- 'Electronics': 'category-1',
71
- 'Clothing': 'category-2',
72
- 'Accessories': 'category-3',
73
- 'Home & Garden': 'category-4',
74
- 'Home': 'category-4', // Alias for Home & Garden
75
- 'Sports & Fitness': 'category-5',
76
- 'Sports': 'category-5', // Alias for Sports & Fitness
77
- 'Books & Media': 'category-6',
78
- 'Toys & Games': 'category-7',
79
- 'Beauty & Health': 'category-8',
80
- 'Automotive': 'category-9',
81
- 'Food & Beverages': 'category-10',
82
- 'Furniture': 'category-4', // Map Furniture to Home & Garden
83
- 'Office': 'category-6', // Map Office to Books & Media (or could be separate)
84
- 'Footwear': 'category-2' // Map Footwear to Clothing
84
+ 'Electronics': 1,
85
+ 'Clothing': 2,
86
+ 'Accessories': 3,
87
+ 'Home & Garden': 4,
88
+ 'Home': 4,
89
+ 'Sports & Fitness': 5,
90
+ 'Sports': 5,
91
+ 'Books & Media': 6,
92
+ 'Toys & Games': 7,
93
+ 'Beauty & Health': 8,
94
+ 'Automotive': 9,
95
+ 'Food & Beverages': 10,
96
+ 'Furniture': 4,
97
+ 'Office': 6,
98
+ 'Footwear': 2
85
99
  };
86
100
 
87
101
  // Product data with images from picsum.photos (reliable placeholder service)
@@ -140,7 +154,7 @@ function generateMockProducts(count = 20) {
140
154
  const productImageId = productTemplate.imageId;
141
155
 
142
156
  // Get categoryId from category name using categoryMap
143
- const categoryId = categoryMap[productCategory] || 'category-1'; // Default to Electronics if not found
157
+ const categoryId = categoryMap[productCategory] || 1;
144
158
 
145
159
  // Determine stock quantity - mix of low stock, high stock, and out of stock
146
160
  const stockType = i % 5;
@@ -179,10 +193,10 @@ function generateMockProducts(count = 20) {
179
193
  const variantPrice = basePrice + (variantIndex % 3 === 0 ? 500 : 0); // Some variants slightly more expensive
180
194
  const variantMrp = Math.round(variantPrice * 1.5);
181
195
  const variantStock = Math.floor(Math.random() * stock) + (stock > 0 ? 1 : 0);
182
- const variantProductId = (i + 1) * 1000 + variantIndex; // Unique productId for variant
196
+ const variantProductId = (i + 1) * 1000 + variantIndex;
183
197
  variants.push({
184
198
  id: `variant-${i + 1}-${variantIndex}`,
185
- productId: variantProductId, // Required by product-card template
199
+ productId: variantProductId,
186
200
  title: `${size} / ${color}`,
187
201
  option1: size,
188
202
  option2: color,
@@ -190,14 +204,15 @@ function generateMockProducts(count = 20) {
190
204
  option2Name: 'Color',
191
205
  price: variantPrice,
192
206
  compareAtPrice: variantMrp,
193
- // prices object required by product-card template
194
207
  prices: {
195
208
  price: variantPrice,
196
209
  mrp: variantMrp
197
210
  },
198
211
  sku: `SKU-${i + 1}-${size}-${color}`,
199
212
  inStock: variantStock > 0,
200
- stock: variantStock,
213
+ stockQuantity: variantStock,
214
+ stockTrackingIsEnabled: true,
215
+ isAllowToOrder: false,
201
216
  available: variantStock > 0
202
217
  });
203
218
  variantIndex++;
@@ -206,7 +221,7 @@ function generateMockProducts(count = 20) {
206
221
  } else if (hasSizeVariations) {
207
222
  // Product with size variations only
208
223
  sizes.slice(0, 5).forEach((size, idx) => {
209
- const variantPrice = basePrice + (idx * 200); // Larger sizes cost more
224
+ const variantPrice = basePrice + (idx * 200);
210
225
  const variantMrp = Math.round(variantPrice * 1.5);
211
226
  const variantStock = Math.floor(Math.random() * stock) + (stock > 0 ? 1 : 0);
212
227
  const variantProductId = (i + 1) * 1000 + idx + 1;
@@ -224,14 +239,16 @@ function generateMockProducts(count = 20) {
224
239
  },
225
240
  sku: `SKU-${i + 1}-${size}`,
226
241
  inStock: variantStock > 0,
227
- stock: variantStock,
242
+ stockQuantity: variantStock,
243
+ stockTrackingIsEnabled: true,
244
+ isAllowToOrder: false,
228
245
  available: variantStock > 0
229
246
  });
230
247
  });
231
248
  } else if (hasColorVariations) {
232
249
  // Product with color variations only
233
250
  colors.slice(0, 4).forEach((color, idx) => {
234
- const variantPrice = basePrice + (idx % 2 === 0 ? 300 : 0); // Some colors cost more
251
+ const variantPrice = basePrice + (idx % 2 === 0 ? 300 : 0);
235
252
  const variantMrp = Math.round(variantPrice * 1.5);
236
253
  const variantStock = Math.floor(Math.random() * stock) + (stock > 0 ? 1 : 0);
237
254
  const variantProductId = (i + 1) * 1000 + idx + 1;
@@ -249,7 +266,9 @@ function generateMockProducts(count = 20) {
249
266
  },
250
267
  sku: `SKU-${i + 1}-${color}`,
251
268
  inStock: variantStock > 0,
252
- stock: variantStock,
269
+ stockQuantity: variantStock,
270
+ stockTrackingIsEnabled: true,
271
+ isAllowToOrder: false,
253
272
  available: variantStock > 0
254
273
  });
255
274
  });
@@ -268,49 +287,44 @@ function generateMockProducts(count = 20) {
268
287
  },
269
288
  sku: `SKU-${i + 1}`,
270
289
  inStock: inStock,
271
- stock: stock,
290
+ stockQuantity: stock,
291
+ stockTrackingIsEnabled: true,
292
+ isAllowToOrder: false,
272
293
  available: inStock
273
294
  });
274
295
  }
275
296
 
276
- // Calculate total stock from variants
277
- const totalStock = variants.reduce((sum, v) => sum + (v.stock || 0), 0);
297
+ const totalStock = variants.reduce((sum, v) => sum + (v.stockQuantity || 0), 0);
278
298
  const hasAnyStock = variants.some(v => v.inStock || v.available);
279
299
 
280
- const productHandle = `product-${i + 1}`.toLowerCase();
300
+ const productHandle = productName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '') + (i > 0 ? `-${i + 1}` : '');
301
+ const productTitle = productName + (i > 0 ? ` ${i + 1}` : '');
281
302
  products.push({
282
- id: productId,
283
- productId: i + 1, // Numeric product ID for API compatibility
284
- title: productName + (i > 0 ? ` ${i + 1}` : ''),
285
- name: productName + (i > 0 ? ` ${i + 1}` : ''), // Also provide name for templates
303
+ id: i + 1,
304
+ productId: i + 1,
305
+ title: productTitle,
306
+ name: productTitle,
286
307
  handle: productHandle,
287
308
  slug: productHandle,
288
- url: `/products/${productHandle}`, // URL for product detail page
289
- link: `/products/${productHandle}`, // Alternative URL property
309
+ url: productHandle,
310
+ link: productHandle,
290
311
  description: `Description for ${productName}. High quality product with excellent features.${hasVariations ? ' Available in multiple options.' : ''}`,
291
312
  price: basePrice,
292
313
  compareAtPrice: comparePrice,
293
- // Prices object used by product-card snippet
294
314
  prices: {
295
315
  price: basePrice,
296
- mrp: comparePrice,
297
- currency: 'USD'
316
+ mrp: comparePrice
298
317
  },
299
- discountPercentage: Math.floor(((comparePrice - basePrice) / comparePrice) * 100),
300
- currency: 'USD',
301
318
  inStock: hasAnyStock,
302
- available: hasAnyStock, // Alternative property for availability
303
- stock: totalStock,
304
- stockQuantity: totalStock, // Alternative property for stock
319
+ available: hasAnyStock,
320
+ stockQuantity: totalStock,
321
+ stockTrackingIsEnabled: true,
322
+ isAllowToOrder: false,
305
323
  sku: `SKU-${i + 1}`,
306
- categoryId: categoryId, // Use mapped categoryId based on product category
307
- category: productCategory, // Also include category name for reference
308
- brandId: `brand-${(i % 8) + 1}`,
309
- // Primary thumbnail for product cards
310
- thumbnailImage1: {
311
- url: `https://picsum.photos/seed/${productImageId}/800/800`,
312
- altText: productName
313
- },
324
+ categoryId: categoryId,
325
+ category: { id: categoryId, name: productCategory },
326
+ brandId: (i % 8) + 1,
327
+ thumbnailImage: `https://picsum.photos/seed/${productImageId}/800/800`,
314
328
  imageUrl: `https://picsum.photos/seed/${productImageId}/800/800`,
315
329
  images: [
316
330
  {
@@ -351,23 +365,19 @@ function generateMockProducts(count = 20) {
351
365
  : [],
352
366
  tags: ['featured', 'new', 'sale'],
353
367
  createdAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(),
354
- // Required fields for product-detail JSON template
355
- productType: 0, // 0 = simple product, 1 = variable, 2 = subscription
356
- // additionalData uses | default:null without | json, so we use string 'null'
357
- // This outputs as literal null in JSON (without quotes)
368
+ productType: 0,
358
369
  additionalData: 'null',
359
- // These fields use | json filter, so they should be actual arrays
360
370
  combinations: [],
361
371
  subscriptions: [],
362
372
  shippingMethods: [],
363
- variations: variants // Also expose as variations for template compatibility
373
+ variations: variants
364
374
  });
365
375
  }
366
376
 
367
377
  // Log summary
368
378
  const productsWithVariations = products.filter(p => (p.variants?.length || 0) > 1).length;
369
379
  const productsWithOptions = products.filter(p => (p.options?.length || 0) > 0).length;
370
- const outOfStock = products.filter(p => !p.inStock || (p.stock || 0) === 0).length;
380
+ const outOfStock = products.filter(p => !p.inStock || (p.stockQuantity || 0) === 0).length;
371
381
 
372
382
  // Log category distribution
373
383
  const categoryDistribution = {};
@@ -410,14 +420,24 @@ function generateMockCategories(count = 10) {
410
420
 
411
421
  for (let i = 0; i < count; i++) {
412
422
  const catData = categoryData[i] || { name: `Category ${i + 1}`, imageId: 60 + i, description: `Products in Category ${i + 1}` };
423
+ const catHandle = catData.name.toLowerCase().replace(/\s+/g, '-').replace(/&/g, 'and');
424
+ const catImageUrl = `https://picsum.photos/seed/cat${catData.imageId}/400/300`;
413
425
  categories.push({
414
- id: `category-${i + 1}`,
426
+ id: i + 1,
427
+ categoryId: i + 1,
415
428
  name: catData.name,
416
- handle: catData.name.toLowerCase().replace(/\s+/g, '-').replace(/&/g, 'and'),
429
+ displayName: catData.name,
430
+ handle: catHandle,
431
+ slug: catHandle,
417
432
  description: catData.description,
418
- image: `https://picsum.photos/seed/cat${catData.imageId}/400/300`,
419
- productCount: Math.floor(Math.random() * 50) + 5,
420
- parentId: i > 5 ? `category-${Math.floor(Math.random() * 5) + 1}` : null
433
+ thumbnailImage: { url: catImageUrl },
434
+ bannerImage: { url: catImageUrl },
435
+ menuImage: { url: catImageUrl },
436
+ displayOrder: i + 1,
437
+ parentCategoryId: i > 5 ? Math.floor(Math.random() * 5) + 1 : null,
438
+ parentName: i > 5 ? categoryData[Math.floor(Math.random() * 5)]?.name || null : null,
439
+ products: [],
440
+ productsCount: Math.floor(Math.random() * 50) + 5
421
441
  });
422
442
  }
423
443
 
@@ -447,9 +467,11 @@ function generateMockBrands(count = 10) {
447
467
  for (let i = 0; i < count; i++) {
448
468
  const brand = brandData[i] || { name: `Brand ${i + 1}`, tagline: 'Quality Products', imageId: 80 + i };
449
469
  brands.push({
450
- id: `brand-${i + 1}`,
470
+ id: i + 1,
471
+ brandId: i + 1,
451
472
  name: brand.name,
452
473
  handle: brand.name.toLowerCase().replace(/\s+/g, '-'),
474
+ slug: brand.name.toLowerCase().replace(/\s+/g, '-'),
453
475
  description: `${brand.tagline}. Premium products from ${brand.name}`,
454
476
  logo: `https://picsum.photos/seed/brand${brand.imageId}/200/100`,
455
477
  image: `https://picsum.photos/seed/brand${brand.imageId}/400/200`,
@@ -473,6 +495,7 @@ function generateMockWidgets() {
473
495
  type: 'Header',
474
496
  section: 'header',
475
497
  sectionName: 'header',
498
+ pageId: 'all',
476
499
  status: 'active',
477
500
  Position: 1,
478
501
  settings: {
@@ -494,6 +517,7 @@ function generateMockWidgets() {
494
517
  type: 'HeaderMenu',
495
518
  section: 'header',
496
519
  sectionName: 'header',
520
+ pageId: 'all',
497
521
  status: 'active',
498
522
  Position: 2,
499
523
  settings: {
@@ -514,6 +538,7 @@ function generateMockWidgets() {
514
538
  type: 'Carousel',
515
539
  section: 'hero',
516
540
  sectionName: 'hero',
541
+ pageId: 'home',
517
542
  status: 'active',
518
543
  Position: 1,
519
544
  settings: {
@@ -584,6 +609,7 @@ function generateMockWidgets() {
584
609
  type: 'SpaceBar',
585
610
  section: 'hero',
586
611
  sectionName: 'hero',
612
+ pageId: 'home',
587
613
  status: 'active',
588
614
  Position: 2,
589
615
  settings: {
@@ -654,6 +680,7 @@ function generateMockWidgets() {
654
680
  type: 'CategoryList',
655
681
  section: 'content',
656
682
  sectionName: 'content',
683
+ pageId: 'home',
657
684
  status: 'active',
658
685
  Position: 1,
659
686
  Title: 'Shop by Category',
@@ -701,6 +728,7 @@ function generateMockWidgets() {
701
728
  type: 'ProductCarousel',
702
729
  section: 'content',
703
730
  sectionName: 'content',
731
+ pageId: 'home',
704
732
  status: 'active',
705
733
  Position: 2,
706
734
  Title: 'Featured Products',
@@ -739,6 +767,7 @@ function generateMockWidgets() {
739
767
  type: 'ProductCarousel',
740
768
  section: 'content',
741
769
  sectionName: 'content',
770
+ pageId: 'home',
742
771
  status: 'active',
743
772
  Position: 3,
744
773
  Title: 'Best Sellers',
@@ -775,6 +804,7 @@ function generateMockWidgets() {
775
804
  type: 'ProductCarousel',
776
805
  section: 'content',
777
806
  sectionName: 'content',
807
+ pageId: 'home',
778
808
  status: 'active',
779
809
  Position: 4,
780
810
  Title: 'New Arrivals',
@@ -810,6 +840,7 @@ function generateMockWidgets() {
810
840
  type: 'ProductCarousel',
811
841
  section: 'content',
812
842
  sectionName: 'content',
843
+ pageId: 'home',
813
844
  status: 'active',
814
845
  Position: 5,
815
846
  Title: 'On Sale',
@@ -847,6 +878,7 @@ function generateMockWidgets() {
847
878
  type: 'ProductCarousel',
848
879
  section: 'content',
849
880
  sectionName: 'content',
881
+ pageId: 'home',
850
882
  status: 'active',
851
883
  Position: 6,
852
884
  Title: 'Trending Now',
@@ -882,6 +914,7 @@ function generateMockWidgets() {
882
914
  type: 'ProductCarousel',
883
915
  section: 'content',
884
916
  sectionName: 'content',
917
+ pageId: 'home',
885
918
  status: 'active',
886
919
  Position: 7,
887
920
  Title: 'Electronics',
@@ -918,6 +951,7 @@ function generateMockWidgets() {
918
951
  type: 'ProductCarousel',
919
952
  section: 'content',
920
953
  sectionName: 'content',
954
+ pageId: 'home',
921
955
  status: 'active',
922
956
  Position: 8,
923
957
  Title: 'Fashion',
@@ -954,6 +988,7 @@ function generateMockWidgets() {
954
988
  type: 'ProductCarousel',
955
989
  section: 'content',
956
990
  sectionName: 'content',
991
+ pageId: 'product',
957
992
  status: 'active',
958
993
  Position: 9,
959
994
  Title: 'You May Also Like',
@@ -988,6 +1023,7 @@ function generateMockWidgets() {
988
1023
  type: 'ProductCarousel',
989
1024
  section: 'content',
990
1025
  sectionName: 'content',
1026
+ pageId: 'product',
991
1027
  status: 'active',
992
1028
  Position: 10,
993
1029
  Title: 'Frequently Bought Together',
@@ -1023,6 +1059,7 @@ function generateMockWidgets() {
1023
1059
  type: 'ProductCarousel',
1024
1060
  section: 'content',
1025
1061
  sectionName: 'content',
1062
+ pageId: 'home',
1026
1063
  status: 'active',
1027
1064
  Position: 11,
1028
1065
  Title: "Editor's Choice",
@@ -1060,6 +1097,7 @@ function generateMockWidgets() {
1060
1097
  type: 'ProductGrid',
1061
1098
  section: 'content',
1062
1099
  sectionName: 'content',
1100
+ pageId: 'home',
1063
1101
  status: 'active',
1064
1102
  Position: 12,
1065
1103
  Title: 'Shop All Products',
@@ -1096,6 +1134,7 @@ function generateMockWidgets() {
1096
1134
  type: 'ProductGrid',
1097
1135
  section: 'content',
1098
1136
  sectionName: 'content',
1137
+ pageId: 'home',
1099
1138
  status: 'active',
1100
1139
  Position: 13,
1101
1140
  Title: 'Featured Collection',
@@ -1133,6 +1172,7 @@ function generateMockWidgets() {
1133
1172
  type: 'Banner',
1134
1173
  section: 'content',
1135
1174
  sectionName: 'content',
1175
+ pageId: 'home',
1136
1176
  status: 'active',
1137
1177
  Position: 4,
1138
1178
  Title: 'Special Offers',
@@ -1171,6 +1211,7 @@ function generateMockWidgets() {
1171
1211
  type: 'SimpleProduct',
1172
1212
  section: 'content',
1173
1213
  sectionName: 'content',
1214
+ pageId: 'home',
1174
1215
  status: 'active',
1175
1216
  Position: 5,
1176
1217
  Title: 'Best Sellers',
@@ -1195,6 +1236,7 @@ function generateMockWidgets() {
1195
1236
  type: 'SingleProduct',
1196
1237
  section: 'content',
1197
1238
  sectionName: 'content',
1239
+ pageId: 'home',
1198
1240
  status: 'active',
1199
1241
  Position: 6,
1200
1242
  Title: 'Deal of the Day',
@@ -1217,6 +1259,7 @@ function generateMockWidgets() {
1217
1259
  type: 'RecentlyViewed',
1218
1260
  section: 'content',
1219
1261
  sectionName: 'content',
1262
+ pageId: 'product',
1220
1263
  status: 'active',
1221
1264
  Position: 7,
1222
1265
  Title: 'Recently Viewed',
@@ -1238,6 +1281,7 @@ function generateMockWidgets() {
1238
1281
  type: 'BrandCarousel',
1239
1282
  section: 'content',
1240
1283
  sectionName: 'content',
1284
+ pageId: 'home',
1241
1285
  status: 'active',
1242
1286
  Position: 8,
1243
1287
  Title: 'Our Brands',
@@ -1283,6 +1327,7 @@ function generateMockWidgets() {
1283
1327
  type: 'Gallery',
1284
1328
  section: 'content',
1285
1329
  sectionName: 'content',
1330
+ pageId: 'home',
1286
1331
  status: 'active',
1287
1332
  Position: 4,
1288
1333
  Title: 'Instagram Gallery',
@@ -1323,6 +1368,7 @@ function generateMockWidgets() {
1323
1368
  type: 'TestimonialCarousel',
1324
1369
  section: 'content',
1325
1370
  sectionName: 'content',
1371
+ pageId: 'home',
1326
1372
  status: 'active',
1327
1373
  Position: 5,
1328
1374
  Title: 'What Our Customers Say',
@@ -1408,6 +1454,7 @@ function generateMockWidgets() {
1408
1454
  type: 'Footer',
1409
1455
  section: 'footer',
1410
1456
  sectionName: 'footer',
1457
+ pageId: 'all',
1411
1458
  status: 'active',
1412
1459
  Position: 1,
1413
1460
  settings: {
@@ -1475,6 +1522,7 @@ function generateMockWidgets() {
1475
1522
  type: 'FooterMenu',
1476
1523
  section: 'footer',
1477
1524
  sectionName: 'footer',
1525
+ pageId: 'all',
1478
1526
  status: 'active',
1479
1527
  Position: 2,
1480
1528
  settings: {
@@ -305,6 +305,55 @@ class WidgetService {
305
305
  }
306
306
  }
307
307
 
308
+ /**
309
+ * Get widgets for a specific page and section.
310
+ * Matches production API: POST /pages/{pageId}/sections/{section}/widgets
311
+ * @param {string} pageId - Page ID (home, product, category, products, categories, brand, checkout)
312
+ * @param {string} section - Section name (header, hero, content, footer)
313
+ * @returns {Promise<Array>} Array of normalized widgets sorted by Position
314
+ */
315
+ async getPageSectionWidgets(pageId, section) {
316
+ try {
317
+ if (!this.apiClient) return [];
318
+
319
+ const response = await this.apiClient.post(`/pages/${pageId}/sections/${section}/widgets`, { status: 'active' });
320
+ const widgets = Array.isArray(response) ? response : (response.widgets || []);
321
+ return this.normalizeWidgetArray(widgets);
322
+ } catch (error) {
323
+ console.warn(`[WidgetService] Error fetching widgets for page=${pageId} section=${section}:`, error.message);
324
+ return [];
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Fetch all widgets for a page, organized by section.
330
+ * Mirrors the production fetchPageWidgets() helper.
331
+ * @param {string} pageId - Page ID
332
+ * @returns {Promise<Object>} Widgets organized by section { header:[], hero:[], content:[], footer:[] }
333
+ */
334
+ async fetchPageWidgets(pageId) {
335
+ const validPageIds = ['home', 'product', 'category', 'products', 'categories', 'brand', 'checkout'];
336
+ if (!pageId || !validPageIds.includes(pageId)) {
337
+ console.warn(`[WidgetService] Invalid pageId: ${pageId}, returning empty widgets`);
338
+ return { header: [], hero: [], content: [], footer: [] };
339
+ }
340
+
341
+ const sections = ['header', 'hero', 'content', 'footer'];
342
+ const results = await Promise.allSettled(
343
+ sections.map(section => this.getPageSectionWidgets(pageId, section))
344
+ );
345
+
346
+ const organized = {};
347
+ sections.forEach((section, index) => {
348
+ const result = results[index];
349
+ organized[section] = (result.status === 'fulfilled' && Array.isArray(result.value)) ? result.value : [];
350
+ });
351
+
352
+ const counts = sections.map(s => `${s}:${organized[s].length}`).join(', ');
353
+ console.log(`[WidgetService] fetchPageWidgets(${pageId}): ${counts}`);
354
+ return organized;
355
+ }
356
+
308
357
  /**
309
358
  * Check if widget template exists in theme
310
359
  * @param {string} widgetType - Widget type
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o2vend/theme-cli",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "O2VEND Theme Development CLI - Standalone tool for local theme development",
5
5
  "bin": {
6
6
  "o2vend": "./bin/o2vend"