@liquidcommerce/elements-sdk 2.2.0-beta.4 → 2.2.0-beta.40

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 (79) hide show
  1. package/README.md +1715 -569
  2. package/dist/index.esm.js +11813 -9048
  3. package/dist/types/core/auth.service.d.ts +10 -4
  4. package/dist/types/core/base-component.service.d.ts +3 -0
  5. package/dist/types/core/circuit-breaker.service.d.ts +54 -0
  6. package/dist/types/core/client/client-action.service.d.ts +16 -13
  7. package/dist/types/core/client/client-config.service.d.ts +5 -3
  8. package/dist/types/core/command/common-command.service.d.ts +2 -1
  9. package/dist/types/core/debug-panel/debug-panel.service.d.ts +43 -0
  10. package/dist/types/core/debug-panel/debug-panel.styles.d.ts +1 -0
  11. package/dist/types/core/fingerprint.service.d.ts +4 -9
  12. package/dist/types/core/google-tag-manager.service.d.ts +127 -2
  13. package/dist/types/core/logger/logger-factory.d.ts +3 -0
  14. package/dist/types/core/logger/logger.service.d.ts +8 -5
  15. package/dist/types/core/pubsub/interfaces/cart.interface.d.ts +1 -0
  16. package/dist/types/core/pubsub/interfaces/checkout.interface.d.ts +46 -6
  17. package/dist/types/core/pubsub/interfaces/core.interface.d.ts +2 -3
  18. package/dist/types/core/pubsub/interfaces/product.interface.d.ts +43 -6
  19. package/dist/types/core/pubsub/pubsub.service.d.ts +2 -1
  20. package/dist/types/core/singleton-manager.service.d.ts +3 -3
  21. package/dist/types/core/store/interfaces/cart.interface.d.ts +1 -1
  22. package/dist/types/core/store/interfaces/checkout.interface.d.ts +0 -1
  23. package/dist/types/core/store/interfaces/core.interface.d.ts +2 -2
  24. package/dist/types/core/store/interfaces/product.interface.d.ts +18 -7
  25. package/dist/types/core/store/store.service.d.ts +2 -1
  26. package/dist/types/core/telemetry/telemetry.interface.d.ts +80 -0
  27. package/dist/types/core/telemetry/telemetry.service.d.ts +27 -0
  28. package/dist/types/elements-base-client.d.ts +49 -0
  29. package/dist/types/elements-builder-client.d.ts +2 -0
  30. package/dist/types/elements-client-helper.d.ts +3 -0
  31. package/dist/types/enums/core.enum.d.ts +0 -1
  32. package/dist/types/enums/debug.enum.d.ts +6 -0
  33. package/dist/types/enums/index.d.ts +1 -0
  34. package/dist/types/index.d.ts +1 -0
  35. package/dist/types/index.umd.d.ts +2 -2
  36. package/dist/types/interfaces/cloud/product.interface.d.ts +2 -0
  37. package/dist/types/interfaces/configs/checkout.interface.d.ts +1 -1
  38. package/dist/types/interfaces/configs/product.interface.d.ts +2 -0
  39. package/dist/types/interfaces/core.interface.d.ts +34 -19
  40. package/dist/types/modules/address/address.command.d.ts +1 -3
  41. package/dist/types/modules/api-client/api-client.service.d.ts +1 -0
  42. package/dist/types/modules/cart/cart.commands.d.ts +1 -1
  43. package/dist/types/modules/cart/cart.component.d.ts +1 -2
  44. package/dist/types/modules/cart/components/cart-footer.component.d.ts +1 -0
  45. package/dist/types/modules/cart/components/cart-item.component.d.ts +2 -6
  46. package/dist/types/modules/cart/components/index.d.ts +2 -0
  47. package/dist/types/modules/checkout/checkout.commands.d.ts +3 -2
  48. package/dist/types/modules/checkout/checkout.component.d.ts +1 -2
  49. package/dist/types/modules/checkout/components/checkout-summary-section.component.d.ts +2 -0
  50. package/dist/types/modules/checkout/components/information/checkout-delivery-information-form.component.d.ts +1 -1
  51. package/dist/types/modules/checkout/components/summary/checkout-item-quantity.component.d.ts +0 -2
  52. package/dist/types/modules/checkout/components/summary/checkout-item.component.d.ts +2 -1
  53. package/dist/types/modules/checkout/components/summary/checkout-items.component.d.ts +1 -0
  54. package/dist/types/modules/checkout/components/summary/checkout-place-order-button.component.d.ts +0 -1
  55. package/dist/types/modules/checkout/constant.d.ts +0 -1
  56. package/dist/types/modules/product/components/index.d.ts +1 -0
  57. package/dist/types/modules/product/components/product-add-to-cart-section.component.d.ts +1 -0
  58. package/dist/types/modules/product/components/product-interactions.component.d.ts +4 -1
  59. package/dist/types/modules/product/components/product-price.component.d.ts +1 -0
  60. package/dist/types/modules/product/components/product-retailers.component.d.ts +1 -0
  61. package/dist/types/modules/product/product.commands.d.ts +1 -1
  62. package/dist/types/modules/theme-provider/services/font-manager.service.d.ts +1 -0
  63. package/dist/types/modules/ui-components/engraving/engraving-form.component.d.ts +13 -11
  64. package/dist/types/modules/ui-components/engraving/engraving-view.component.d.ts +4 -9
  65. package/dist/types/modules/ui-components/lce-element/lce-element.component.d.ts +2 -0
  66. package/dist/types/static/icon/index.d.ts +0 -1
  67. package/dist/types/utils/format.d.ts +2 -1
  68. package/docs/ACTIONS.md +1300 -0
  69. package/docs/BROWSER_SUPPORT.md +279 -0
  70. package/docs/CONFIGURATION.md +740 -0
  71. package/docs/DOCUMENTATION_INDEX.md +311 -0
  72. package/docs/EVENTS.md +765 -0
  73. package/docs/PROXY.md +228 -0
  74. package/docs/THEMING.md +592 -0
  75. package/docs/TROUBLESHOOTING.md +793 -0
  76. package/package.json +18 -18
  77. package/umd/elements.js +1 -1
  78. package/dist/types/modules/product/constant.d.ts +0 -2
  79. package/dist/types/static/icon/completed.icon.d.ts +0 -2
@@ -0,0 +1,1300 @@
1
+ # Elements SDK Actions - Partner Control Guide
2
+
3
+ > **Browser Support**: This SDK supports 2018+ browsers (Chrome 66+, Firefox 60+, Safari 12+) with comprehensive polyfills. See `docs/BROWSER_SUPPORT.md` for details.
4
+
5
+ ## What Are Actions and Why Use Them?
6
+
7
+ Actions let you control the LiquidCommerce components programmatically - like having a remote control for your e-commerce experience. Instead of waiting for customers to click buttons, you can make things happen automatically based on your business logic.
8
+
9
+ **Business Benefits:**
10
+ - **Automate User Flow**: Guide customers through the purchase process
11
+ - **Custom Experiences**: Build unique checkout flows that match your brand
12
+ - **Reduce Cart Abandonment**: Pre-fill information and remove friction
13
+ - **Increase Conversions**: Trigger actions at the perfect moment
14
+ - **Integrate with Existing Systems**: Connect to your CRM, inventory, etc.
15
+
16
+ ## How It Works & Getting Feedback
17
+
18
+ Actions are **fire-and-forget** methods that return `void` or `Promise<void>`. Instead of return values, **all feedback comes through events** - this is by design for security and flexibility.
19
+
20
+ ```javascript
21
+ // Actions are available globally
22
+ const actions = window.elements.actions;
23
+
24
+ // 1. Fire the action (simple, clean)
25
+ await actions.cart.applyPromoCode('SAVE20');
26
+
27
+ // 2. Listen for success/failure feedback via events
28
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
29
+ const { applied, discountAmount, newTotal } = event.detail.data;
30
+ showSuccessMessage(`🎉 Promo applied! You saved $${discountAmount}!`);
31
+ updateCartDisplay(newTotal);
32
+ });
33
+
34
+ window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
35
+ const { attempted, error } = event.detail.data;
36
+ showErrorMessage('Promo code could not be applied. Please try again.');
37
+ });
38
+ ```
39
+
40
+ ### 🔒 **Security-First Design**
41
+
42
+ Events **never expose sensitive information** like promo codes, gift card codes, or balance details that could be intercepted by malicious scripts. You get all the feedback you need without security risks.
43
+
44
+ **✅ Safe Data in Events:**
45
+ - Success/failure status
46
+ - Discount amounts (already visible in UI)
47
+ - Total amounts (already visible in UI)
48
+ - Item counts and identifiers
49
+ - Generic error messages
50
+
51
+ **🚫 Never Exposed:**
52
+ - Promo codes or gift card codes
53
+ - Gift card balances or details
54
+ - Specific error messages that reveal codes
55
+ - Any sensitive financial information
56
+
57
+ ## Your Action Toolkit
58
+
59
+ ### 🛍️ Product Control (`actions.product`)
60
+
61
+ **What it does**: Get information about products to make smart business decisions
62
+
63
+ #### Get Product Details
64
+ ```javascript
65
+ // Check product info before making decisions
66
+ const product = actions.product.getDetails('product-123');
67
+
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) {
75
+ showPremiumSupport();
76
+ }
77
+
78
+ if (product.sizes && Object.keys(product.sizes).length > 0) {
79
+ enableAddToCart();
80
+ } else {
81
+ showWaitlistSignup();
82
+ }
83
+ ```
84
+
85
+ **Real Business Uses:**
86
+ - **Dynamic Pricing**: Show different offers based on product price
87
+ - **Inventory Management**: Hide out-of-stock items or show alternatives
88
+ - **Personalization**: Recommend accessories based on main product
89
+ - **A/B Testing**: Show different CTAs for different product categories
90
+
91
+ ### 📍 Address Control (`actions.address`)
92
+
93
+ **What it does**: Manage shipping addresses to reduce checkout friction
94
+
95
+ #### Set Address by Google Places ID
96
+ ```javascript
97
+ // Set address using Google Places ID - fastest way to capture accurate addresses
98
+ try {
99
+ await actions.address.setAddressByPlacesId('ChIJ0SRjyK5ZwokRp1TwT8dJSv8');
100
+ showMessage("Address set successfully!");
101
+ } catch (error) {
102
+ showMessage("Address not found. Please try again.");
103
+ }
104
+
105
+ // Real-world example: Let customers pick from their recent locations
106
+ const recentPlacesIds = getCustomerRecentPlaces();
107
+ if (recentPlacesIds.length > 0) {
108
+ // Use their most recent address automatically
109
+ await actions.address.setAddressByPlacesId(recentPlacesIds[0]);
110
+ }
111
+ ```
112
+
113
+ **Find Google Places IDs**: Use the [Google Places ID Finder](https://developers.google.com/maps/documentation/places/web-service/place-id#find-id) to get Place IDs for any location.
114
+
115
+ #### Set Address Manually
116
+ ```javascript
117
+ // Set address manually without Google Places - perfect for custom address forms
118
+ try {
119
+ await actions.address.setAddressManually(
120
+ {
121
+ one: '123 Main St',
122
+ two: 'Apt 4B', // Optional apartment/suite
123
+ city: 'New York',
124
+ state: 'NY',
125
+ zip: '10001',
126
+ country: 'United States' // Optional, will be included in formatted address
127
+ },
128
+ {
129
+ lat: 40.7505045, // Latitude coordinate
130
+ long: -73.9934387 // Longitude coordinate
131
+ }
132
+ );
133
+ showMessage("Manual address set successfully!");
134
+ } catch (error) {
135
+ showMessage("Invalid address or coordinates. Please check and try again.");
136
+ }
137
+
138
+ // Real-world example: Custom address form integration
139
+ function handleCustomAddressSubmit(formData) {
140
+ const addressData = {
141
+ one: formData.streetAddress,
142
+ two: formData.apartment || '', // Optional
143
+ city: formData.city,
144
+ state: formData.state,
145
+ zip: formData.zipCode,
146
+ country: formData.country || 'United States'
147
+ };
148
+
149
+ const coordinates = {
150
+ lat: parseFloat(formData.latitude),
151
+ long: parseFloat(formData.longitude)
152
+ };
153
+
154
+ // Validate coordinates before setting
155
+ if (coordinates.lat >= -90 && coordinates.lat <= 90 &&
156
+ coordinates.long >= -180 && coordinates.long <= 180) {
157
+ await actions.address.setAddressManually(addressData, coordinates);
158
+ } else {
159
+ showMessage("Invalid coordinates provided");
160
+ }
161
+ }
162
+
163
+ // Integration with geocoding services
164
+ async function setAddressWithGeocoding(streetAddress, city, state, zip) {
165
+ try {
166
+ // Use your preferred geocoding service to get coordinates
167
+ const coordinates = await geocodeAddress(`${streetAddress}, ${city}, ${state} ${zip}`);
168
+
169
+ await actions.address.setAddressManually(
170
+ {
171
+ one: streetAddress,
172
+ two: '',
173
+ city: city,
174
+ state: state,
175
+ zip: zip,
176
+ country: 'United States'
177
+ },
178
+ coordinates
179
+ );
180
+
181
+ showMessage("Address geocoded and set successfully!");
182
+ } catch (error) {
183
+ showMessage("Could not geocode address. Please verify the address.");
184
+ }
185
+ }
186
+ ```
187
+
188
+ **Key Features:**
189
+ - **Automatic Formatting**: Generates Google Places API-formatted address strings automatically
190
+ - **Empty Places ID**: Places ID is set to empty string (perfect for non-Google Places addresses)
191
+ - **Flexible Input**: Works with any address structure and coordinate source
192
+ - **Validation**: Built-in validation for required fields and coordinate ranges
193
+ - **Same Events**: Triggers the same success/failure events as Google Places addresses
194
+
195
+ #### Check Address
196
+ ```javascript
197
+ // See if customer has an address saved
198
+ const address = actions.address.getDetails();
199
+
200
+ if (address) {
201
+ // Address is saved - express checkout available!
202
+ showExpressCheckout();
203
+ } else {
204
+ // No address - guide them through address entry
205
+ highlightAddressForm();
206
+ }
207
+ ```
208
+
209
+ #### Clear Address
210
+ ```javascript
211
+ // Clear address (useful for guest checkout option)
212
+ actions.address.clear();
213
+ ```
214
+
215
+ **Complete Reset Behavior:**
216
+ The `actions.address.clear()` action performs a comprehensive reset of the user's address and shopping session:
217
+
218
+ **What gets cleared:**
219
+ - ✅ **Address Data**: All saved address information (street, city, state, zip, coordinates, Places ID)
220
+ - ✅ **Cart Contents**: Complete cart reset (removes all items, totals, promo codes, retailers)
221
+ - ✅ **Local Storage**: Completely removes the localStorage entry and its value
222
+ - ✅ **Database**: Deletes the persisted store from the server database
223
+ - ✅ **Checkout State**: Resets any pending checkout information
224
+
225
+ **Why the cart is reset:**
226
+ When an address is cleared, the cart must be reset because:
227
+ - Cart items have location-specific pricing and availability
228
+ - Fulfillment options are tied to specific addresses
229
+ - Delivery fees and shipping costs depend on location
230
+ - Without a valid address, cart operations would fail or show incorrect data
231
+
232
+ **Events fired:**
233
+ - `lce:actions.address_cleared` - Address successfully cleared
234
+ - `lce:actions.cart_reset` - Cart successfully reset
235
+
236
+ **Use cases:**
237
+ - Guest checkout option (clear previous user's data)
238
+ - Location change (start fresh with new address)
239
+ - Privacy compliance (complete data removal)
240
+ - Testing/development (reset to clean state)
241
+
242
+ **Real Business Uses:**
243
+ - **One-Click Address**: Let customers select from saved Google Places
244
+ - **Custom Address Forms**: Build your own address input forms with full control
245
+ - **Third-Party Integration**: Connect with non-Google geocoding services
246
+ - **Legacy System Migration**: Import addresses from existing customer databases
247
+ - **International Addresses**: Handle addresses that may not be in Google Places
248
+ - **Location-Based Services**: Auto-detect customer location and set address
249
+ - **Express Checkout**: Skip address forms for returning customers
250
+ - **Shipping Calculator**: Calculate shipping costs before checkout
251
+ - **Local Pickup**: Show pickup options based on their address
252
+ - **Tax Calculation**: Display accurate taxes before purchase
253
+
254
+ ## 📡 **Complete Action Feedback Events Reference**
255
+
256
+ All actions provide feedback through events. Here are all available success/failure events:
257
+
258
+ ### **Cart Action Events**
259
+ ```javascript
260
+ // Product management
261
+ 'lce:actions.cart_product_add_success' // { itemsAdded: number, identifiers: string[] }
262
+ 'lce:actions.cart_product_add_failed' // { identifiers: string[], error: string }
263
+
264
+ // Promo code management
265
+ 'lce:actions.cart_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
266
+ 'lce:actions.cart_promo_code_removed' // { applied: false }
267
+ 'lce:actions.cart_promo_code_failed' // { attempted: true, error: string }
268
+ ```
269
+
270
+ ### **Checkout Action Events**
271
+ ```javascript
272
+ // Product management
273
+ 'lce:actions.checkout_product_add_success' // { itemsAdded: number, identifiers: string[] }
274
+ 'lce:actions.checkout_product_add_failed' // { identifiers: string[], error: string }
275
+
276
+ // Promo code management
277
+ 'lce:actions.checkout_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
278
+ 'lce:actions.checkout_promo_code_removed' // { applied: false }
279
+ 'lce:actions.checkout_promo_code_failed' // { attempted: true, error: string }
280
+
281
+ // Gift card management
282
+ 'lce:actions.checkout_gift_card_applied' // { applied: true, newTotal?: number }
283
+ 'lce:actions.checkout_gift_card_removed' // { applied: false }
284
+ 'lce:actions.checkout_gift_card_failed' // { attempted: true, error: string }
285
+ ```
286
+
287
+ ### **Address Action Events**
288
+ ```javascript
289
+ // Address management (already available)
290
+ 'lce:actions.address_updated' // { googlePlacesId: string, formattedAddress: string, address: {...}, coordinates: {...} }
291
+ 'lce:actions.address_failed' // { error: string, googlePlacesId?: string }
292
+ 'lce:actions.address_cleared' // boolean
293
+ ```
294
+
295
+ ### **Event Handling Best Practices**
296
+
297
+ #### **1. Set Up Listeners Before Actions**
298
+ ```javascript
299
+ // ✅ Good: Set up listener first
300
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
301
+ await actions.cart.applyPromoCode('SAVE20');
302
+
303
+ // ❌ Bad: Listener after action (might miss event)
304
+ await actions.cart.applyPromoCode('SAVE20');
305
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
306
+ ```
307
+
308
+ #### **2. Handle Both Success and Failure**
309
+ ```javascript
310
+ // ✅ Always handle both outcomes
311
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
312
+ window.addEventListener('lce:actions.cart_promo_code_failed', handleFailure);
313
+ await actions.cart.applyPromoCode('SAVE20');
314
+ ```
315
+
316
+ #### **3. Use Event Data Effectively**
317
+ ```javascript
318
+ // ✅ Use all available event data
319
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
320
+ const { itemsAdded, identifiers } = event.detail.data;
321
+
322
+ // Show success message
323
+ showMessage(`Added ${itemsAdded} items to cart`);
324
+
325
+ // Track analytics
326
+ analytics.track('Products Added', { items: identifiers, count: itemsAdded });
327
+
328
+ // Update UI
329
+ updateCartCount();
330
+ showUpsellRecommendations(identifiers);
331
+ });
332
+ ```
333
+
334
+ #### **4. Create Reusable Event Handlers**
335
+ ```javascript
336
+ // ✅ Create reusable handlers
337
+ const handlePromoSuccess = (event) => {
338
+ const { applied, discountAmount, newTotal } = event.detail.data;
339
+ showSuccessMessage(`Discount applied! Saved: $${discountAmount}`);
340
+ updatePriceDisplay(newTotal);
341
+ trackPromoSuccess(discountAmount);
342
+ };
343
+
344
+ const handlePromoFailure = (event) => {
345
+ const { attempted, error } = event.detail.data;
346
+ showErrorMessage('Promo code could not be applied');
347
+ trackPromoFailure();
348
+ };
349
+
350
+ // Use for both cart and checkout
351
+ window.addEventListener('lce:actions.cart_promo_code_applied', handlePromoSuccess);
352
+ window.addEventListener('lce:actions.checkout_promo_code_applied', handlePromoSuccess);
353
+ window.addEventListener('lce:actions.cart_promo_code_failed', handlePromoFailure);
354
+ window.addEventListener('lce:actions.checkout_promo_code_failed', handlePromoFailure);
355
+ ```
356
+
357
+ ### 🛒 Cart Control (`actions.cart`)
358
+
359
+ **What it does**: Control the shopping cart to optimize conversions
360
+
361
+ #### Show/Hide Cart
362
+ ```javascript
363
+ // Show cart at strategic moments
364
+ actions.cart.openCart(); // Open cart
365
+ actions.cart.closeCart(); // Close cart
366
+ actions.cart.toggleCart(); // Toggle visibility
367
+
368
+ // Example: Open cart automatically after second product add
369
+ let addToCartCount = 0;
370
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
371
+ addToCartCount++;
372
+ if (addToCartCount === 2) {
373
+ actions.cart.openCart(); // "Look what you've got!"
374
+ }
375
+ });
376
+ ```
377
+
378
+ #### Programmatically Add Products to Cart
379
+ ```javascript
380
+ // Add products directly to cart without product component
381
+ await actions.cart.addProduct([
382
+ {
383
+ identifier: 'product-123', // Product UPC, ID, or Salsify grouping
384
+ fulfillmentType: 'shipping', // or 'onDemand'
385
+ quantity: 2
386
+ },
387
+ {
388
+ identifier: 'product-456',
389
+ fulfillmentType: 'onDemand',
390
+ quantity: 1
391
+ }
392
+ ], true); // Optional: open cart after adding (default: false)
393
+
394
+ // Listen for success/failure feedback
395
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
396
+ const { itemsAdded, identifiers } = event.detail.data;
397
+ console.log(`✅ Added ${itemsAdded} products:`, identifiers);
398
+ showSuccessMessage('Products added to cart!');
399
+ });
400
+
401
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
402
+ const { identifiers, error } = event.detail.data;
403
+ console.log(`❌ Failed to add products:`, identifiers, error);
404
+ showErrorMessage('Could not add products. Please try again.');
405
+ });
406
+ ```
407
+
408
+ **Real Business Use**: Perfect for "Buy Now" buttons, quick add flows, bundle purchases, or any scenario where you want to add products without displaying the full product component first.
409
+
410
+ #### Check Cart Contents
411
+ ```javascript
412
+ // See what's in the cart to make smart decisions
413
+ const cart = actions.cart.getDetails();
414
+
415
+ if (cart.amounts.total > 100) {
416
+ showFreeShippingBanner();
417
+ }
418
+
419
+ if (cart.itemCount === 0) {
420
+ showPopularProducts();
421
+ }
422
+ ```
423
+
424
+ #### Add Products to Cart with Event Feedback
425
+ ```javascript
426
+ // Add products programmatically
427
+ await actions.cart.addProduct([{
428
+ identifier: 'product-123',
429
+ fulfillmentType: 'shipping',
430
+ quantity: 1
431
+ }]);
432
+
433
+ // Listen for success
434
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
435
+ const { itemsAdded, identifiers } = event.detail.data;
436
+ showMessage(`✅ Added ${itemsAdded} items to cart!`);
437
+
438
+ // Track conversion
439
+ analytics.track('Products Added to Cart', {
440
+ items: identifiers,
441
+ count: itemsAdded
442
+ });
443
+
444
+ // Maybe show related products
445
+ showRelatedProducts(identifiers);
446
+ });
447
+
448
+ // Listen for failures
449
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
450
+ const { identifiers, error } = event.detail.data;
451
+ showErrorMessage('Could not add some products. Please try again.');
452
+
453
+ // Log for debugging
454
+ console.error('Failed to add products:', identifiers, error);
455
+
456
+ // Show alternatives
457
+ showAlternativeProducts(identifiers);
458
+ });
459
+
460
+ // Add bundles or cross-sells
461
+ await actions.cart.addProduct([
462
+ { identifier: 'phone-123', fulfillmentType: 'shipping', quantity: 1 },
463
+ { identifier: 'case-456', fulfillmentType: 'shipping', quantity: 1 },
464
+ { identifier: 'charger-789', fulfillmentType: 'shipping', quantity: 1 }
465
+ ]);
466
+ ```
467
+
468
+ #### Apply Discounts with Event Feedback
469
+ ```javascript
470
+ // Apply promo codes automatically
471
+ await actions.cart.applyPromoCode('WELCOME10');
472
+
473
+ // Listen for success
474
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
475
+ const { applied, discountAmount, newTotal } = event.detail.data;
476
+ showMessage(`Welcome discount applied! You saved $${discountAmount}!`);
477
+ updateCartDisplay(newTotal);
478
+
479
+ // Track successful promo usage
480
+ analytics.track('Promo Applied', { discount: discountAmount });
481
+ });
482
+
483
+ // Listen for failure and try fallback
484
+ window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
485
+ const { attempted, error } = event.detail.data;
486
+ console.log('First promo failed, trying backup promo');
487
+
488
+ // Try another strategy
489
+ actions.cart.applyPromoCode('FIRSTTIME');
490
+ });
491
+
492
+ // Remove promo code with feedback
493
+ await actions.cart.removePromoCode();
494
+
495
+ // Listen for removal confirmation
496
+ window.addEventListener('lce:actions.cart_promo_code_removed', function(event) {
497
+ const { applied } = event.detail.data;
498
+ showMessage("Previous promo removed. Applying better discount...");
499
+
500
+ // Apply better deal
501
+ actions.cart.applyPromoCode('BETTER20');
502
+ });
503
+ ```
504
+
505
+ #### Reset Cart
506
+ ```javascript
507
+ // Clear cart for fresh start
508
+ await actions.cart.resetCart();
509
+ ```
510
+
511
+ **Real Business Uses:**
512
+ - **Smart Cart Timing**: Open cart when customer is most likely to buy
513
+ - **Bundle Sales**: Add complementary products automatically
514
+ - **Discount Strategy**: Apply best available discount automatically
515
+ - **Abandonment Prevention**: Show cart contents at right moment
516
+
517
+ ### 💳 Checkout Control (`actions.checkout`)
518
+
519
+ **What it does**: Streamline the checkout process to maximize conversions
520
+
521
+ #### Control Checkout Flow
522
+ ```javascript
523
+ // Open checkout at the perfect moment
524
+ actions.checkout.openCheckout();
525
+ actions.checkout.closeCheckout();
526
+ actions.checkout.toggleCheckout();
527
+
528
+ // Example: Open checkout automatically for high-value carts
529
+ const cart = actions.cart.getDetails();
530
+ if (cart.amounts.total > 500) {
531
+ actions.checkout.openCheckout(); // VIP checkout experience
532
+ }
533
+ ```
534
+
535
+ #### Programmatically Add Products to Checkout
536
+ ```javascript
537
+ // Add products directly to checkout (bypasses cart, goes straight to checkout)
538
+ await actions.checkout.addProduct([
539
+ {
540
+ identifier: 'product-123',
541
+ fulfillmentType: 'shipping',
542
+ quantity: 1
543
+ }
544
+ ], true); // Optional: open checkout after adding (default: false)
545
+
546
+ // Listen for success/failure feedback
547
+ window.addEventListener('lce:actions.checkout_product_add_success', function(event) {
548
+ const { itemsAdded, identifiers } = event.detail.data;
549
+ console.log(`✅ Added ${itemsAdded} products to checkout:`, identifiers);
550
+ showSuccessMessage('Ready for checkout!');
551
+ });
552
+
553
+ window.addEventListener('lce:actions.checkout_product_add_failed', function(event) {
554
+ const { identifiers, error } = event.detail.data;
555
+ console.log(`❌ Failed to add products to checkout:`, identifiers, error);
556
+ showErrorMessage('Could not proceed to checkout. Please try again.');
557
+ });
558
+ ```
559
+
560
+ #### Add Presale Product to Checkout
561
+ ```javascript
562
+ // Add a single presale product directly to checkout (bypasses cart, always opens checkout drawer)
563
+ // The method automatically validates product availability and fulfillment options
564
+ await actions.checkout.addPresaleProduct({
565
+ identifier: 'presale-product-123',
566
+ fulfillmentType: 'shipping',
567
+ quantity: 1
568
+ });
569
+
570
+ // Listen for success/failure feedback (same events as regular addProduct)
571
+ window.addEventListener('lce:actions.checkout_product_add_success', function(event) {
572
+ const { itemsAdded, identifiers, isPresale } = event.detail.data;
573
+ if (isPresale) {
574
+ console.log(`✅ Added ${itemsAdded} presale product to checkout:`, identifiers);
575
+ showSuccessMessage('Presale item ready for checkout!');
576
+ }
577
+ });
578
+
579
+ window.addEventListener('lce:actions.checkout_product_add_failed', function(event) {
580
+ const { identifiers, error, isPresale } = event.detail.data;
581
+ if (isPresale) {
582
+ console.log(`❌ Failed to add presale product to checkout:`, identifiers, error);
583
+ showErrorMessage('Could not proceed with presale checkout. Please try again.');
584
+ }
585
+ });
586
+ ```
587
+
588
+ **Key Features:**
589
+ - **Product Availability Validation**: Automatically fetches and validates product data
590
+ - **Fulfillment Type Support**: Checks if the requested fulfillment type is available
591
+ - **Presale Verification**: Ensures the product is actually a presale item
592
+ - **Always Opens Checkout**: Automatically opens checkout drawer after adding presale product
593
+ - **Address Requirement**: Opens address input if location is not available
594
+ - **Error Handling**: Comprehensive error handling with detailed feedback
595
+
596
+ **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.
597
+
598
+ #### Pre-fill Customer Information
599
+ ```javascript
600
+ // Speed up checkout by pre-filling known information
601
+ actions.checkout.updateCustomerInfo({
602
+ firstName: 'John',
603
+ lastName: 'Doe',
604
+ email: 'john@example.com',
605
+ phone: '+1234567890'
606
+ });
607
+
608
+ // Pre-fill billing from your CRM
609
+ const customerData = getCRMData(customerId);
610
+ actions.checkout.updateBillingInfo({
611
+ firstName: customerData.firstName,
612
+ lastName: customerData.lastName,
613
+ street1: customerData.address,
614
+ city: customerData.city,
615
+ state: customerData.state,
616
+ zipCode: customerData.zip
617
+ });
618
+ ```
619
+
620
+ #### Smart Discount Application with Event Feedback
621
+ ```javascript
622
+ // Apply the best available discount automatically
623
+ const customer = getCurrentCustomer();
624
+
625
+ // Set up event listeners first
626
+ window.addEventListener('lce:actions.checkout_promo_code_applied', function(event) {
627
+ const { applied, discountAmount, newTotal } = event.detail.data;
628
+ showCheckoutSuccess(`Discount applied! You saved $${discountAmount}`);
629
+ updateCheckoutTotal(newTotal);
630
+
631
+ // Track successful conversion optimization
632
+ analytics.track('Checkout Discount Applied', { discount: discountAmount });
633
+ });
634
+
635
+ window.addEventListener('lce:actions.checkout_promo_code_failed', function(event) {
636
+ const { attempted, error } = event.detail.data;
637
+ console.log('Promo code failed:', error);
638
+
639
+ // Try fallback strategies or notify user
640
+ showCheckoutError('Discount could not be applied');
641
+ });
642
+
643
+ // Apply gift card event listeners
644
+ window.addEventListener('lce:actions.checkout_gift_card_applied', function(event) {
645
+ const { applied, newTotal } = event.detail.data;
646
+ showCheckoutSuccess('Gift card applied to your order!');
647
+ updateCheckoutTotal(newTotal);
648
+ });
649
+
650
+ window.addEventListener('lce:actions.checkout_gift_card_failed', function(event) {
651
+ const { attempted, error } = event.detail.data;
652
+ showCheckoutError('Gift card could not be applied. Please check and try again.');
653
+ });
654
+
655
+ // Now apply the discounts
656
+ if (customer.isFirstTime) {
657
+ await actions.checkout.applyPromoCode('WELCOME20');
658
+ } else if (customer.isVIP) {
659
+ await actions.checkout.applyPromoCode('VIP15');
660
+ }
661
+
662
+ // Apply gift cards automatically
663
+ if (customer.hasGiftCard) {
664
+ await actions.checkout.applyGiftCard(customer.giftCardCode);
665
+ }
666
+ ```
667
+
668
+ #### Optimize Checkout Options
669
+ ```javascript
670
+ // Smart defaults based on customer behavior
671
+ if (customer.prefersGifts) {
672
+ await actions.checkout.toggleIsGift(true);
673
+ }
674
+
675
+ // Auto-enable marketing for engaged customers
676
+ if (customer.engagementScore > 8) {
677
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
678
+ actions.checkout.toggleMarketingPreferences('canSms', true);
679
+ }
680
+
681
+ // Smart billing address handling
682
+ if (customer.billingAddress === customer.shippingAddress) {
683
+ await actions.checkout.toggleBillingSameAsShipping(true);
684
+ }
685
+ ```
686
+
687
+ #### Get Checkout Details
688
+ ```javascript
689
+ // Get safe, non-sensitive checkout information
690
+ const checkout = actions.checkout.getDetails();
691
+
692
+ // Make business decisions based on checkout state
693
+ if (checkout.amounts.total > 1000) {
694
+ // High-value order - offer white glove service
695
+ showWhiteGloveService();
696
+ }
697
+
698
+ if (checkout.itemCount > 5) {
699
+ // Large order - offer bulk discount
700
+ await actions.checkout.applyPromoCode('BULK15');
701
+ }
702
+
703
+ if (checkout.isGift && checkout.hasAgeVerify) {
704
+ // Gift order with age verification - show age verification requirements
705
+ showAgeVerificationNotice();
706
+ }
707
+
708
+ if (!checkout.hasPromoCode) {
709
+ // No promo code applied - suggest best available discount
710
+ suggestBestPromoCode(checkout.amounts.total);
711
+ }
712
+
713
+ if (checkout.hasGiftCards && checkout.amounts.total < 50) {
714
+ // Gift cards applied but low total - suggest adding more items
715
+ showUpsellSuggestions();
716
+ }
717
+
718
+ // Available checkout details (all non-sensitive):
719
+ // - amounts: { total, subtotal, shipping, discounts, tax, etc. }
720
+ // - items: { [itemId]: { name, price, quantity, brand, etc. } }
721
+ // - isGift: boolean, hasAgeVerify: boolean
722
+ // - hasPromoCode: boolean, hasGiftCards: boolean
723
+ // - itemCount: number
724
+ ```
725
+
726
+ **Real Business Uses:**
727
+ - **Reduce Checkout Time**: Pre-fill known customer information
728
+ - **Maximize Discounts**: Apply best available offer automatically
729
+ - **VIP Experience**: Different checkout flow for high-value customers
730
+ - **Gift Optimization**: Auto-enable gift options for gift buyers
731
+
732
+ ## Real-World Business Scenarios
733
+
734
+ ### 🎯 Scenario 1: VIP Customer Experience
735
+ ```javascript
736
+ // Detect VIP customers and give them special treatment
737
+ async function handleVIPCustomer(customer) {
738
+ if (customer.totalSpent > 5000) {
739
+ // Pre-fill everything for express checkout
740
+ actions.checkout.updateCustomerInfo(customer.profile);
741
+ actions.checkout.updateBillingInfo(customer.billingAddress);
742
+
743
+ // Apply VIP discount automatically
744
+ await actions.checkout.applyPromoCode('VIP20');
745
+
746
+ // Enable premium features
747
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
748
+
749
+ // Open checkout immediately - no cart friction
750
+ actions.checkout.openCheckout();
751
+
752
+ showVIPMessage("Welcome back! Your VIP checkout is ready.");
753
+ }
754
+ }
755
+ ```
756
+
757
+ ### 🛍️ Scenario 2: Smart Bundle Sales
758
+ ```javascript
759
+ // Automatically add complementary products based on specific product identifiers
760
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
761
+ const data = event.detail.data;
762
+
763
+ // Define cross-sell rules by identifier
764
+ const crossSellMap = {
765
+ 'laptop-001': ['laptop-case-123', 'wireless-mouse-456'],
766
+ 'camera-001': ['camera-bag-789', 'sd-card-512gb']
767
+ };
768
+
769
+ // Check if this product has recommended accessories
770
+ if (crossSellMap[data.identifier]) {
771
+ const accessories = crossSellMap[data.identifier];
772
+ await actions.cart.addProduct(
773
+ accessories.map(id => ({
774
+ identifier: id,
775
+ fulfillmentType: 'shipping',
776
+ quantity: 1
777
+ }))
778
+ );
779
+
780
+ showMessage("We've added recommended accessories to your cart!");
781
+ }
782
+ });
783
+ ```
784
+
785
+ ### 📱 Scenario 3: Mobile-First Quick Checkout
786
+ ```javascript
787
+ // Streamlined mobile experience
788
+ function mobileQuickCheckout() {
789
+ if (isMobileDevice()) {
790
+ // Get customer data from their last purchase
791
+ const lastOrder = getLastOrder();
792
+
793
+ if (lastOrder) {
794
+ // Pre-fill everything
795
+ actions.checkout.updateCustomerInfo(lastOrder.customer);
796
+ actions.checkout.updateBillingInfo(lastOrder.billing);
797
+
798
+ // Skip cart, go straight to checkout
799
+ actions.checkout.openCheckout();
800
+
801
+ showMessage("Express checkout ready! Same info as last time.");
802
+ }
803
+ }
804
+ }
805
+
806
+ // Trigger on Add to Cart for mobile users
807
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
808
+ if (isMobileDevice() && isReturningCustomer()) {
809
+ mobileQuickCheckout();
810
+ }
811
+ });
812
+ ```
813
+
814
+ ### 💰 Scenario 4: Dynamic Pricing & Promotions
815
+ ```javascript
816
+ // Apply the best available discount
817
+ async function optimizeCheckout() {
818
+ const cart = actions.cart.getDetails();
819
+ const customer = getCurrentCustomer();
820
+
821
+ // Try different discount strategies
822
+ const discountStrategies = [
823
+ { code: 'BULK25', minTotal: 200 },
824
+ { code: 'RETURNING15', condition: customer.orderCount > 1 },
825
+ { code: 'FIRSTTIME10', condition: customer.orderCount === 0 },
826
+ { code: 'SEASONAL20', condition: isHolidaySeason() }
827
+ ];
828
+
829
+ for (const strategy of discountStrategies) {
830
+ if (strategy.minTotal && cart.amounts.total >= strategy.minTotal) {
831
+ try {
832
+ await actions.checkout.applyPromoCode(strategy.code);
833
+ break; // Success, stop trying
834
+ } catch (error) {
835
+ continue; // Try next strategy
836
+ }
837
+ } else if (strategy.condition) {
838
+ try {
839
+ await actions.checkout.applyPromoCode(strategy.code);
840
+ break;
841
+ } catch (error) {
842
+ continue;
843
+ }
844
+ }
845
+ }
846
+ }
847
+ ```
848
+
849
+ ### 🎁 Scenario 5: Holiday Gift Flow
850
+ ```javascript
851
+ // Optimize for gift purchases during holidays
852
+ async function holidayGiftOptimization() {
853
+ if (isHolidaySeason()) {
854
+ // Auto-enable gift features
855
+ actions.checkout.toggleIsGift(true);
856
+
857
+ // Pre-populate gift message templates
858
+ const giftMessages = [
859
+ "Happy Holidays!",
860
+ "Merry Christmas!",
861
+ "With love and best wishes"
862
+ ];
863
+
864
+ showGiftMessageSuggestions(giftMessages);
865
+
866
+ // Suggest gift wrapping
867
+ showGiftWrappingOption();
868
+
869
+ // Apply holiday shipping discount
870
+ await actions.checkout.applyPromoCode('FREEHOLIDAYSHIP');
871
+ }
872
+ }
873
+
874
+ // Trigger during checkout
875
+ window.addEventListener('lce:actions.checkout_opened', holidayGiftOptimization);
876
+ ```
877
+
878
+ ### 📍 Scenario 6: Smart Address Auto-Complete
879
+ ```javascript
880
+ // Use Google Places to streamline address entry
881
+ async function smartAddressFlow() {
882
+ const customer = getCurrentCustomer();
883
+
884
+ // For returning customers, use their recent addresses
885
+ if (customer.recentPlacesIds && customer.recentPlacesIds.length > 0) {
886
+ try {
887
+ // Auto-set their most recent address
888
+ await actions.address.setAddressByPlacesId(customer.recentPlacesIds[0]);
889
+
890
+ showMessage("We've set your address to your recent location. Change it if needed!");
891
+
892
+ // Show other recent addresses as quick options
893
+ showRecentAddressOptions(customer.recentPlacesIds.slice(1, 4));
894
+
895
+ } catch (error) {
896
+ // Fallback to manual entry
897
+ showAddressForm();
898
+ }
899
+ }
900
+
901
+ // For location-aware features
902
+ if (customer.hasLocationPermission) {
903
+ const nearbyBusinesses = await getNearbyBusinessPlaces();
904
+
905
+ // If they're near a business location, suggest it
906
+ if (nearbyBusinesses.length > 0) {
907
+ showMessage("We found you're near our store location. Use it for pickup?");
908
+
909
+ document.getElementById('use-store-address').onclick = async () => {
910
+ await actions.address.setAddressByPlacesId(nearbyBusinesses[0].placeId);
911
+ showMessage("Store pickup address set!");
912
+ };
913
+ }
914
+ }
915
+ }
916
+
917
+ // Use when address is updated
918
+ smartAddressFlow();
919
+
920
+ // Quick address selection from saved places
921
+ async function useQuickAddress(placeId, displayName) {
922
+ try {
923
+ await actions.address.setAddressByPlacesId(placeId);
924
+ showMessage(`Address set to ${displayName}`);
925
+
926
+ // Automatically proceed to shipping options
927
+ showShippingOptionsForAddress();
928
+
929
+ } catch (error) {
930
+ showMessage("That address is no longer available. Please select another.");
931
+ }
932
+ }
933
+ ```
934
+
935
+ ### 🏢 Scenario 7: Custom Address Form Integration
936
+ ```javascript
937
+ // Integrate with your own address forms and geocoding services
938
+ async function customAddressFormFlow() {
939
+ const customer = getCurrentCustomer();
940
+
941
+ // If customer has saved addresses in your database (not Google Places)
942
+ if (customer.savedAddresses && customer.savedAddresses.length > 0) {
943
+ showSavedAddressOptions(customer.savedAddresses);
944
+ }
945
+
946
+ // Handle custom form submission
947
+ document.getElementById('custom-address-form').addEventListener('submit', async (event) => {
948
+ event.preventDefault();
949
+
950
+ const formData = new FormData(event.target);
951
+ const addressData = {
952
+ one: formData.get('street'),
953
+ two: formData.get('apartment') || '',
954
+ city: formData.get('city'),
955
+ state: formData.get('state'),
956
+ zip: formData.get('zip'),
957
+ country: formData.get('country') || 'United States'
958
+ };
959
+
960
+ try {
961
+ // Use your geocoding service to get coordinates
962
+ const coordinates = await yourGeocodingService.geocode(
963
+ `${addressData.one}, ${addressData.city}, ${addressData.state} ${addressData.zip}`
964
+ );
965
+
966
+ // Set the address manually
967
+ await actions.address.setAddressManually(addressData, {
968
+ lat: coordinates.latitude,
969
+ long: coordinates.longitude
970
+ });
971
+
972
+ showMessage("Your address has been set successfully!");
973
+
974
+ // Save to your database for future use
975
+ saveCustomerAddress(customer.id, addressData, coordinates);
976
+
977
+ } catch (geocodeError) {
978
+ showError("Could not find coordinates for this address. Please verify and try again.");
979
+ }
980
+ });
981
+
982
+ // Handle saved address selection
983
+ async function useSavedAddress(savedAddress) {
984
+ try {
985
+ await actions.address.setAddressManually(
986
+ {
987
+ one: savedAddress.street,
988
+ two: savedAddress.apartment || '',
989
+ city: savedAddress.city,
990
+ state: savedAddress.state,
991
+ zip: savedAddress.zip,
992
+ country: savedAddress.country || 'United States'
993
+ },
994
+ {
995
+ lat: savedAddress.coordinates.lat,
996
+ long: savedAddress.coordinates.long
997
+ }
998
+ );
999
+
1000
+ showMessage(`Address set to your saved location: ${savedAddress.nickname}`);
1001
+
1002
+ } catch (error) {
1003
+ showError("There was an issue with your saved address. Please try entering it again.");
1004
+ }
1005
+ }
1006
+ }
1007
+
1008
+ // Import addresses from legacy system
1009
+ async function importLegacyAddresses() {
1010
+ const legacyAddresses = await getLegacyCustomerAddresses();
1011
+
1012
+ for (const address of legacyAddresses) {
1013
+ try {
1014
+ // Convert legacy format to Elements format
1015
+ const elementsAddress = {
1016
+ one: address.address_line_1,
1017
+ two: address.address_line_2 || '',
1018
+ city: address.city_name,
1019
+ state: address.state_code,
1020
+ zip: address.postal_code,
1021
+ country: address.country_name || 'United States'
1022
+ };
1023
+
1024
+ const coordinates = {
1025
+ lat: address.latitude,
1026
+ long: address.longitude
1027
+ };
1028
+
1029
+ // Set address in Elements system
1030
+ await actions.address.setAddressManually(elementsAddress, coordinates);
1031
+
1032
+ console.log(`Successfully imported address for customer ${address.customer_id}`);
1033
+
1034
+ } catch (error) {
1035
+ console.error(`Failed to import address for customer ${address.customer_id}:`, error);
1036
+ }
1037
+ }
1038
+ }
1039
+
1040
+ // Handle international addresses that might not be in Google Places
1041
+ async function handleInternationalAddress(internationalAddressData) {
1042
+ try {
1043
+ // Use an international geocoding service
1044
+ const coordinates = await internationalGeocodingService.geocode(
1045
+ internationalAddressData.fullAddress,
1046
+ internationalAddressData.country
1047
+ );
1048
+
1049
+ await actions.address.setAddressManually(
1050
+ {
1051
+ one: internationalAddressData.addressLine1,
1052
+ two: internationalAddressData.addressLine2 || '',
1053
+ city: internationalAddressData.city,
1054
+ state: internationalAddressData.region, // Province, state, etc.
1055
+ zip: internationalAddressData.postalCode,
1056
+ country: internationalAddressData.country
1057
+ },
1058
+ coordinates
1059
+ );
1060
+
1061
+ showMessage("International address set successfully!");
1062
+
1063
+ } catch (error) {
1064
+ showError("Could not process this international address. Please contact support.");
1065
+ }
1066
+ }
1067
+ ```
1068
+
1069
+ ## Implementation Strategy
1070
+
1071
+ ### 🚀 Quick Start (5-Minute Setup)
1072
+
1073
+ **Step 1: Choose Your Goal**
1074
+ - Increase conversions? → Focus on cart and checkout actions
1075
+ - Reduce abandonment? → Use pre-filling and automation
1076
+ - Boost sales? → Implement bundle and upsell logic
1077
+ - Improve UX? → Create smart defaults and express flows
1078
+
1079
+ **Step 2: Start Simple**
1080
+ ```javascript
1081
+ // Copy this basic template and customize
1082
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1083
+ const product = event.detail.data;
1084
+
1085
+ // Your business logic here
1086
+ if (product.priceInfo && product.priceInfo.avg > 100) {
1087
+ // High-value product - offer premium support
1088
+ showPremiumSupportOffer();
1089
+ }
1090
+
1091
+ // Maybe add complementary products based on identifier
1092
+ const crossSellMap = {
1093
+ 'phone-001': 'phone-case-recommended',
1094
+ 'laptop-001': 'laptop-case-recommended'
1095
+ };
1096
+
1097
+ if (crossSellMap[product.identifier]) {
1098
+ await actions.cart.addProduct([{
1099
+ identifier: crossSellMap[product.identifier],
1100
+ fulfillmentType: 'shipping',
1101
+ quantity: 1
1102
+ }]);
1103
+ }
1104
+ });
1105
+ ```
1106
+
1107
+ **Step 3: Test & Iterate**
1108
+ 1. Add one action at a time
1109
+ 2. Test with real user flows
1110
+ 3. Monitor conversion impact
1111
+ 4. Adjust based on results
1112
+
1113
+ ### 💡 Pro Implementation Tips
1114
+
1115
+ #### Combine Actions with Events
1116
+ ```javascript
1117
+ // Listen for events, respond with actions
1118
+ window.addEventListener('lce:actions.cart_opened', function() {
1119
+ const cart = actions.cart.getDetails();
1120
+
1121
+ // If cart value is close to free shipping threshold
1122
+ if (cart.amounts.total > 45 && cart.amounts.total < 50) {
1123
+ showFreeShippingUpsell();
1124
+ }
1125
+ });
1126
+
1127
+ // Pre-fill checkout for VIP customers
1128
+ window.addEventListener('lce:actions.checkout_opened', async function() {
1129
+ const customer = getCurrentCustomer();
1130
+
1131
+ if (customer.isVIP) {
1132
+ actions.checkout.updateCustomerInfo(customer.profile);
1133
+ await actions.checkout.applyPromoCode('VIP20');
1134
+ }
1135
+ });
1136
+ ```
1137
+
1138
+ #### Chain Actions for Workflows
1139
+ ```javascript
1140
+ // Create complete automated flows
1141
+ async function expressCheckoutFlow(productId, customerId) {
1142
+ // 1. Add product to cart
1143
+ await actions.cart.addProduct([{
1144
+ identifier: productId,
1145
+ fulfillmentType: 'shipping',
1146
+ quantity: 1
1147
+ }]);
1148
+
1149
+ // 2. Pre-fill customer data
1150
+ const customer = getCustomerData(customerId);
1151
+ actions.checkout.updateCustomerInfo(customer);
1152
+
1153
+ // 3. Apply best discount
1154
+ await actions.checkout.applyPromoCode(getBestDiscountFor(customer));
1155
+
1156
+ // 4. Open checkout
1157
+ actions.checkout.openCheckout();
1158
+
1159
+ showMessage("Express checkout ready! Review and complete your order.");
1160
+ }
1161
+ ```
1162
+
1163
+ #### Smart Error Handling
1164
+ ```javascript
1165
+ // Graceful degradation
1166
+ async function smartAddToCart(productId) {
1167
+ try {
1168
+ await actions.cart.addProduct([{
1169
+ identifier: productId,
1170
+ fulfillmentType: 'shipping',
1171
+ quantity: 1
1172
+ }]);
1173
+
1174
+ showSuccessMessage("Added to cart!");
1175
+ } catch (error) {
1176
+ // Fallback - maybe the product is out of stock
1177
+ showMessage("This item isn't available, but here are similar options:");
1178
+ showAlternativeProducts(productId);
1179
+ }
1180
+ }
1181
+ ```
1182
+
1183
+ ## Common Integration Patterns
1184
+
1185
+ ### 🎯 **Pattern 1: Behavioral Triggers**
1186
+ ```javascript
1187
+ // Trigger actions based on customer behavior
1188
+ let pageViewCount = 0;
1189
+ let timeOnPage = 0;
1190
+
1191
+ // Track engagement
1192
+ setInterval(() => timeOnPage++, 1000);
1193
+
1194
+ // Act based on engagement
1195
+ if (timeOnPage > 60 && pageViewCount > 3) {
1196
+ // Engaged user - show special offer
1197
+ await actions.cart.applyPromoCode('ENGAGED15');
1198
+ showSpecialOffer();
1199
+ }
1200
+ ```
1201
+
1202
+ ### 🛍️ **Pattern 2: Cross-Sell Automation**
1203
+ ```javascript
1204
+ // Automatic product recommendations based on identifier
1205
+ const crossSellRules = {
1206
+ 'laptop-001': ['laptop-case', 'wireless-mouse', 'usb-hub'],
1207
+ 'phone-001': ['phone-case', 'screen-protector', 'wireless-charger'],
1208
+ 'camera-001': ['memory-card', 'camera-bag', 'tripod']
1209
+ };
1210
+
1211
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1212
+ const data = event.detail.data;
1213
+ const recommendations = crossSellRules[data.identifier];
1214
+
1215
+ if (recommendations) {
1216
+ // Add first recommendation automatically
1217
+ await actions.cart.addProduct([{
1218
+ identifier: recommendations[0],
1219
+ fulfillmentType: 'shipping',
1220
+ quantity: 1
1221
+ }]);
1222
+
1223
+ showMessage(`We've added ${recommendations[0]} - customers love this combo!`);
1224
+ }
1225
+ });
1226
+ ```
1227
+
1228
+ ### 💰 **Pattern 3: Dynamic Pricing**
1229
+ ```javascript
1230
+ // Apply best available discount automatically
1231
+ async function optimizePricing() {
1232
+ const cart = actions.cart.getDetails();
1233
+ const customer = getCurrentCustomer();
1234
+ const now = new Date();
1235
+
1236
+ // Time-based discounts
1237
+ if (now.getHours() < 10) {
1238
+ await actions.cart.applyPromoCode('EARLYBIRD10');
1239
+ }
1240
+
1241
+ // Volume discounts
1242
+ else if (cart.amounts.total > 200) {
1243
+ await actions.cart.applyPromoCode('BULK20');
1244
+ }
1245
+
1246
+ // Customer-specific discounts
1247
+ else if (customer.orderCount === 0) {
1248
+ await actions.cart.applyPromoCode('WELCOME15');
1249
+ }
1250
+
1251
+ // Seasonal discounts
1252
+ else if (isBlackFriday()) {
1253
+ await actions.cart.applyPromoCode('BLACKFRIDAY25');
1254
+ }
1255
+ }
1256
+ ```
1257
+
1258
+ ## Business ROI Tracking
1259
+
1260
+ Track the impact of your action implementations:
1261
+
1262
+ ```javascript
1263
+ // Track business metrics
1264
+ const metrics = {
1265
+ automatedDiscountsApplied: 0,
1266
+ bundleSalesCreated: 0,
1267
+ expressCheckoutsCompleted: 0,
1268
+ cartAbandonmentsPrevented: 0
1269
+ };
1270
+
1271
+ // Track when actions create business value
1272
+ window.addEventListener('lce:actions.cart_updated', function() {
1273
+ if (wasDiscountAppliedAutomatically()) {
1274
+ metrics.automatedDiscountsApplied++;
1275
+ }
1276
+ });
1277
+
1278
+ window.addEventListener('lce:actions.checkout_submit_completed', function() {
1279
+ if (wasExpressCheckout()) {
1280
+ metrics.expressCheckoutsCompleted++;
1281
+ }
1282
+ });
1283
+
1284
+ // Send metrics to your analytics
1285
+ setInterval(() => {
1286
+ sendBusinessMetrics(metrics);
1287
+ }, 60000); // Every minute
1288
+ ```
1289
+
1290
+ ## Getting Started Today
1291
+
1292
+ 1. **Pick ONE scenario** from the examples above that matches your biggest business need
1293
+ 2. **Copy the code** and customize it for your products/customers
1294
+ 3. **Test it** with a small group first
1295
+ 4. **Measure the impact** on your conversion rates
1296
+ 5. **Scale up** what works, iterate on what doesn't
1297
+
1298
+ **Remember**: The power is in combining the right actions with the right business logic for YOUR specific use case. Start simple, then build more sophisticated flows as you see results!
1299
+
1300
+ Need help implementing? The actions work automatically once LiquidCommerce Elements are installed - just add your business logic and watch conversions improve!