@liquidcommerce/elements-sdk 2.4.0 → 2.4.2

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/docs/ACTIONS.md CHANGED
@@ -65,16 +65,19 @@ Events **never expose sensitive information** like promo codes, gift card codes,
65
65
  // Check product info before making decisions
66
66
  const product = actions.product.getDetails('product-123');
67
67
 
68
- if (product.price > 100) {
69
- // Show premium support option
68
+ // Use product attributes for recommendations
69
+ if (product.variety === 'Cabernet Sauvignon' && product.region === 'Napa Valley') {
70
+ showSimilarNapaWines();
71
+ }
72
+
73
+ // Pricing and availability
74
+ if (product.priceInfo && product.priceInfo.avg > 100) {
70
75
  showPremiumSupport();
71
76
  }
72
77
 
73
- if (product.isAvailable) {
74
- // Enable add to cart
78
+ if (product.sizes && Object.keys(product.sizes).length > 0) {
75
79
  enableAddToCart();
76
80
  } else {
77
- // Show waitlist signup
78
81
  showWaitlistSignup();
79
82
  }
80
83
  ```
@@ -382,13 +385,11 @@ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
382
385
  // See what's in the cart to make smart decisions
383
386
  const cart = actions.cart.getDetails();
384
387
 
385
- if (cart.total > 100) {
386
- // Offer free shipping
388
+ if (cart.amounts.total > 100) {
387
389
  showFreeShippingBanner();
388
390
  }
389
391
 
390
- if (cart.items.length === 0) {
391
- // Cart is empty - show recommendations
392
+ if (cart.itemCount === 0) {
392
393
  showPopularProducts();
393
394
  }
394
395
  ```
@@ -499,7 +500,7 @@ actions.checkout.toggleCheckout();
499
500
 
500
501
  // Example: Open checkout automatically for high-value carts
501
502
  const cart = actions.cart.getDetails();
502
- if (cart.total > 500) {
503
+ if (cart.amounts.total > 500) {
503
504
  actions.checkout.openCheckout(); // VIP checkout experience
504
505
  }
505
506
  ```
@@ -529,7 +530,43 @@ window.addEventListener('lce:actions.checkout_product_add_failed', function(even
529
530
  });
530
531
  ```
531
532
 
532
- **Real Business Use**: Perfect for "Buy Now" buttons, one-click purchasing, express checkout flows, or any scenario where you want to skip the cart and go straight to checkout.
533
+ #### Add Presale Product to Checkout
534
+ ```javascript
535
+ // Add a single presale product directly to checkout (bypasses cart, always opens checkout drawer)
536
+ // The method automatically validates product availability and fulfillment options
537
+ await actions.checkout.addPresaleProduct({
538
+ identifier: 'presale-product-123',
539
+ fulfillmentType: 'shipping',
540
+ quantity: 1
541
+ });
542
+
543
+ // Listen for success/failure feedback (same events as regular addProduct)
544
+ window.addEventListener('lce:actions.checkout_product_add_success', function(event) {
545
+ const { itemsAdded, identifiers, isPresale } = event.detail.data;
546
+ if (isPresale) {
547
+ console.log(`✅ Added ${itemsAdded} presale product to checkout:`, identifiers);
548
+ showSuccessMessage('Presale item ready for checkout!');
549
+ }
550
+ });
551
+
552
+ window.addEventListener('lce:actions.checkout_product_add_failed', function(event) {
553
+ const { identifiers, error, isPresale } = event.detail.data;
554
+ if (isPresale) {
555
+ console.log(`❌ Failed to add presale product to checkout:`, identifiers, error);
556
+ showErrorMessage('Could not proceed with presale checkout. Please try again.');
557
+ }
558
+ });
559
+ ```
560
+
561
+ **Key Features:**
562
+ - **Product Availability Validation**: Automatically fetches and validates product data
563
+ - **Fulfillment Type Support**: Checks if the requested fulfillment type is available
564
+ - **Presale Verification**: Ensures the product is actually a presale item
565
+ - **Always Opens Checkout**: Automatically opens checkout drawer after adding presale product
566
+ - **Address Requirement**: Opens address input if location is not available
567
+ - **Error Handling**: Comprehensive error handling with detailed feedback
568
+
569
+ **Real Business Use**: Perfect for "Buy Now" buttons, one-click purchasing, express checkout flows, or any scenario where you want to skip the cart and go straight to checkout. The `addPresaleProduct` method is specifically designed for presale items that need to bypass the regular cart flow and go directly to checkout.
533
570
 
534
571
  #### Pre-fill Customer Information
535
572
  ```javascript
@@ -625,14 +662,6 @@ if (customer.billingAddress === customer.shippingAddress) {
625
662
  // Get safe, non-sensitive checkout information
626
663
  const checkout = actions.checkout.getDetails();
627
664
 
628
- // Check checkout status and totals
629
- console.log('Checkout total:', checkout.amounts.total);
630
- console.log('Items in checkout:', checkout.itemCount);
631
- console.log('Is gift order:', checkout.isGift);
632
- console.log('Has age verification:', checkout.hasAgeVerify);
633
- console.log('Has promo code applied:', checkout.hasPromoCode);
634
- console.log('Has gift cards applied:', checkout.hasGiftCards);
635
-
636
665
  // Make business decisions based on checkout state
637
666
  if (checkout.amounts.total > 1000) {
638
667
  // High-value order - offer white glove service
@@ -700,24 +729,28 @@ async function handleVIPCustomer(customer) {
700
729
 
701
730
  ### 🛍️ Scenario 2: Smart Bundle Sales
702
731
  ```javascript
703
- // Automatically add complementary products
732
+ // Automatically add complementary products based on specific product identifiers
704
733
  window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
705
- const product = event.detail.data;
734
+ const data = event.detail.data;
735
+
736
+ // Define cross-sell rules by identifier
737
+ const crossSellMap = {
738
+ 'laptop-001': ['laptop-case-123', 'wireless-mouse-456'],
739
+ 'camera-001': ['camera-bag-789', 'sd-card-512gb']
740
+ };
706
741
 
707
- // If they bought a laptop, add accessories
708
- if (product.category === 'laptops') {
709
- const cart = actions.cart.getDetails();
710
- const hasAccessories = cart.items.some(item => item.category === 'accessories');
742
+ // Check if this product has recommended accessories
743
+ if (crossSellMap[data.identifier]) {
744
+ const accessories = crossSellMap[data.identifier];
745
+ await actions.cart.addProduct(
746
+ accessories.map(id => ({
747
+ identifier: id,
748
+ fulfillmentType: 'shipping',
749
+ quantity: 1
750
+ }))
751
+ );
711
752
 
712
- if (!hasAccessories) {
713
- // Add recommended accessories bundle
714
- await actions.cart.addProduct([
715
- { identifier: 'laptop-case-123', fulfillmentType: 'shipping', quantity: 1 },
716
- { identifier: 'wireless-mouse-456', fulfillmentType: 'shipping', quantity: 1 }
717
- ]);
718
-
719
- showMessage("We've added recommended accessories to your cart!");
720
- }
753
+ showMessage("We've added recommended accessories to your cart!");
721
754
  }
722
755
  });
723
756
  ```
@@ -767,7 +800,7 @@ async function optimizeCheckout() {
767
800
  ];
768
801
 
769
802
  for (const strategy of discountStrategies) {
770
- if (strategy.minTotal && cart.total >= strategy.minTotal) {
803
+ if (strategy.minTotal && cart.amounts.total >= strategy.minTotal) {
771
804
  try {
772
805
  await actions.checkout.applyPromoCode(strategy.code);
773
806
  break; // Success, stop trying
@@ -1023,15 +1056,20 @@ window.addEventListener('lce:actions.product_add_to_cart', async function(event)
1023
1056
  const product = event.detail.data;
1024
1057
 
1025
1058
  // Your business logic here
1026
- if (product.price > 100) {
1059
+ if (product.priceInfo && product.priceInfo.avg > 100) {
1027
1060
  // High-value product - offer premium support
1028
1061
  showPremiumSupportOffer();
1029
1062
  }
1030
1063
 
1031
- // Maybe add complementary products
1032
- if (product.category === 'phones') {
1064
+ // Maybe add complementary products based on identifier
1065
+ const crossSellMap = {
1066
+ 'phone-001': 'phone-case-recommended',
1067
+ 'laptop-001': 'laptop-case-recommended'
1068
+ };
1069
+
1070
+ if (crossSellMap[product.identifier]) {
1033
1071
  await actions.cart.addProduct([{
1034
- identifier: 'phone-case-recommended',
1072
+ identifier: crossSellMap[product.identifier],
1035
1073
  fulfillmentType: 'shipping',
1036
1074
  quantity: 1
1037
1075
  }]);
@@ -1054,7 +1092,7 @@ window.addEventListener('lce:actions.cart_opened', function() {
1054
1092
  const cart = actions.cart.getDetails();
1055
1093
 
1056
1094
  // If cart value is close to free shipping threshold
1057
- if (cart.total > 45 && cart.total < 50) {
1095
+ if (cart.amounts.total > 45 && cart.amounts.total < 50) {
1058
1096
  showFreeShippingUpsell();
1059
1097
  }
1060
1098
  });
@@ -1136,16 +1174,16 @@ if (timeOnPage > 60 && pageViewCount > 3) {
1136
1174
 
1137
1175
  ### 🛍️ **Pattern 2: Cross-Sell Automation**
1138
1176
  ```javascript
1139
- // Automatic product recommendations
1177
+ // Automatic product recommendations based on identifier
1140
1178
  const crossSellRules = {
1141
- 'laptop': ['laptop-case', 'wireless-mouse', 'usb-hub'],
1142
- 'phone': ['phone-case', 'screen-protector', 'wireless-charger'],
1143
- 'camera': ['memory-card', 'camera-bag', 'tripod']
1179
+ 'laptop-001': ['laptop-case', 'wireless-mouse', 'usb-hub'],
1180
+ 'phone-001': ['phone-case', 'screen-protector', 'wireless-charger'],
1181
+ 'camera-001': ['memory-card', 'camera-bag', 'tripod']
1144
1182
  };
1145
1183
 
1146
1184
  window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1147
- const product = event.detail.data;
1148
- const recommendations = crossSellRules[product.category];
1185
+ const data = event.detail.data;
1186
+ const recommendations = crossSellRules[data.identifier];
1149
1187
 
1150
1188
  if (recommendations) {
1151
1189
  // Add first recommendation automatically
@@ -1155,7 +1193,7 @@ window.addEventListener('lce:actions.product_add_to_cart', async function(event)
1155
1193
  quantity: 1
1156
1194
  }]);
1157
1195
 
1158
- showMessage(`We've added ${recommendations[0]} - customers who bought ${product.name} love this combo!`);
1196
+ showMessage(`We've added ${recommendations[0]} - customers love this combo!`);
1159
1197
  }
1160
1198
  });
1161
1199
  ```
@@ -1174,7 +1212,7 @@ async function optimizePricing() {
1174
1212
  }
1175
1213
 
1176
1214
  // Volume discounts
1177
- else if (cart.total > 200) {
1215
+ else if (cart.amounts.total > 200) {
1178
1216
  await actions.cart.applyPromoCode('BULK20');
1179
1217
  }
1180
1218
 
@@ -308,34 +308,158 @@ data-env="production"
308
308
  data-debug-mode="console"
309
309
  ```
310
310
 
311
- #### `data-cart-id`
311
+ #### `data-cart-button`
312
312
 
313
313
  **Type:** `string`
314
314
 
315
- Container ID for cart button.
315
+ Container ID, CSS selector, or position-prefixed selector for simple cart button (no badge).
316
316
 
317
317
  ```html
318
- data-cart-id="header-cart"
318
+ <!-- Simple ID -->
319
+ data-cart-button="header-cart"
320
+
321
+ <!-- CSS Selector (from browser inspect tool) -->
322
+ data-cart-button=".header .nav-links"
323
+ data-cart-button="#main-navigation"
324
+
325
+ <!-- Position-prefixed selectors -->
326
+ data-cart-button="above:.header .logo" <!-- Place above the target -->
327
+ data-cart-button="below:#main-navigation" <!-- Place below the target -->
328
+ data-cart-button="replace:.old-cart" <!-- Replace the target -->
329
+ data-cart-button="inside:.header .nav" <!-- Place inside the target (default) -->
319
330
  ```
320
331
 
321
- #### `data-show-cart-items`
332
+ #### `data-cart-badge-button`
322
333
 
323
- **Type:** flag (no value)
334
+ **Type:** `string`
324
335
 
325
- Show item count on cart button.
336
+ Container ID, CSS selector, or position-prefixed selector for cart button with item count badge.
326
337
 
327
338
  ```html
328
- data-show-cart-items
339
+ <!-- Simple ID -->
340
+ data-cart-badge-button="header-cart"
341
+
342
+ <!-- CSS Selector (from browser inspect tool) -->
343
+ data-cart-badge-button=".header .nav-links"
344
+ data-cart-badge-button="#main-navigation"
345
+
346
+ <!-- Position-prefixed selectors -->
347
+ data-cart-badge-button="above:.header .logo" <!-- Place above the target -->
348
+ data-cart-badge-button="below:#main-navigation" <!-- Place below the target -->
349
+ data-cart-badge-button="replace:.old-cart" <!-- Replace the target -->
350
+ data-cart-badge-button="inside:.header .nav" <!-- Place inside the target (default) -->
329
351
  ```
330
352
 
331
- #### `data-hide-cart-floating-button`
353
+ **Position Prefixes:**
354
+ - `above:` - Place above the target element
355
+ - `below:` - Place below the target element
356
+ - `replace:` - Replace the target element
357
+ - `inside:` - Place inside the target element (default)
358
+
359
+ #### `data-cart-button-hidden`
332
360
 
333
361
  **Type:** flag (no value)
334
362
 
335
- Hide the default floating cart button.
363
+ Hide cart button completely.
336
364
 
337
365
  ```html
338
- data-hide-cart-floating-button
366
+ data-cart-button-hidden
367
+ ```
368
+
369
+ ### Cart Button Examples
370
+
371
+ **Simple cart button (no badge):**
372
+ ```html
373
+ <script
374
+ data-liquid-commerce-elements
375
+ data-token="your-api-key"
376
+ data-cart-button="header-cart"
377
+ src="elements.js">
378
+ </script>
379
+ ```
380
+
381
+ **Cart button with item count badge:**
382
+ ```html
383
+ <script
384
+ data-liquid-commerce-elements
385
+ data-token="your-api-key"
386
+ data-cart-badge-button="header-cart"
387
+ src="elements.js">
388
+ </script>
389
+ ```
390
+
391
+ **Using position-prefixed selectors:**
392
+ ```html
393
+ <!-- Place inside the target element (default) -->
394
+ <script
395
+ data-liquid-commerce-elements
396
+ data-token="your-api-key"
397
+ data-cart-badge-button="inside:.header .nav-links"
398
+ src="elements.js">
399
+ </script>
400
+
401
+ <!-- Place above the target element -->
402
+ <script
403
+ data-liquid-commerce-elements
404
+ data-token="your-api-key"
405
+ data-cart-button="above:.header .logo"
406
+ src="elements.js">
407
+ </script>
408
+
409
+ <!-- Place below the target element -->
410
+ <script
411
+ data-liquid-commerce-elements
412
+ data-token="your-api-key"
413
+ data-cart-badge-button="below:#main-navigation"
414
+ src="elements.js">
415
+ </script>
416
+
417
+ <!-- Replace the target element -->
418
+ <script
419
+ data-liquid-commerce-elements
420
+ data-token="your-api-key"
421
+ data-cart-button="replace:.old-cart-button"
422
+ src="elements.js">
423
+ </script>
424
+ ```
425
+
426
+ **No cart button:**
427
+ ```html
428
+ <script
429
+ data-liquid-commerce-elements
430
+ data-token="your-api-key"
431
+ data-cart-button-hidden
432
+ src="elements.js">
433
+ </script>
434
+ ```
435
+
436
+ **Default behavior (floating cart button with badge):**
437
+ ```html
438
+ <script
439
+ data-liquid-commerce-elements
440
+ data-token="your-api-key"
441
+ src="elements.js">
442
+ </script>
443
+ ```
444
+
445
+ ### Getting CSS Selectors from Browser
446
+
447
+ **Super Easy Method:**
448
+ 1. **Right-click** on any element where you want the cart button
449
+ 2. Select **"Inspect"** or **"Inspect Element"**
450
+ 3. In developer tools, **right-click** on the highlighted element
451
+ 4. Select **"Copy" → "Copy selector"**
452
+ 5. Paste the selector into your `data-cart-button` or `data-cart-badge-button` attribute
453
+
454
+ **Example:**
455
+ ```html
456
+ <!-- After copying selector from browser inspect tool -->
457
+ <script
458
+ data-liquid-commerce-elements
459
+ data-token="your-api-key"
460
+ data-cart-badge-button="body > div.container > header > nav > div.nav-links"
461
+ src="elements.js">
462
+ </script>
339
463
  ```
340
464
 
341
465
  ### Product Configuration