@liquidcommerce/elements-sdk 2.2.0-beta.3 → 2.2.0-beta.31

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 (69) hide show
  1. package/README.md +1681 -568
  2. package/dist/index.esm.js +10716 -8124
  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/store/interfaces/cart.interface.d.ts +1 -1
  20. package/dist/types/core/store/interfaces/checkout.interface.d.ts +0 -1
  21. package/dist/types/core/store/interfaces/core.interface.d.ts +2 -2
  22. package/dist/types/core/store/interfaces/product.interface.d.ts +18 -7
  23. package/dist/types/core/store/store.service.d.ts +1 -0
  24. package/dist/types/core/telemetry/telemetry.interface.d.ts +80 -0
  25. package/dist/types/core/telemetry/telemetry.service.d.ts +27 -0
  26. package/dist/types/elements-client-helper.d.ts +3 -0
  27. package/dist/types/enums/core.enum.d.ts +0 -1
  28. package/dist/types/enums/debug.enum.d.ts +6 -0
  29. package/dist/types/enums/index.d.ts +1 -0
  30. package/dist/types/interfaces/cloud/product.interface.d.ts +2 -0
  31. package/dist/types/interfaces/configs/checkout.interface.d.ts +1 -1
  32. package/dist/types/interfaces/core.interface.d.ts +17 -11
  33. package/dist/types/modules/address/address.command.d.ts +1 -3
  34. package/dist/types/modules/cart/cart.commands.d.ts +1 -1
  35. package/dist/types/modules/cart/cart.component.d.ts +1 -2
  36. package/dist/types/modules/cart/components/cart-footer.component.d.ts +1 -0
  37. package/dist/types/modules/cart/components/cart-item.component.d.ts +2 -6
  38. package/dist/types/modules/checkout/checkout.commands.d.ts +3 -2
  39. package/dist/types/modules/checkout/checkout.component.d.ts +1 -2
  40. package/dist/types/modules/checkout/components/checkout-summary-section.component.d.ts +2 -0
  41. package/dist/types/modules/checkout/components/information/checkout-delivery-information-form.component.d.ts +1 -1
  42. package/dist/types/modules/checkout/components/summary/checkout-item-quantity.component.d.ts +0 -2
  43. package/dist/types/modules/checkout/components/summary/checkout-item.component.d.ts +2 -1
  44. package/dist/types/modules/checkout/components/summary/checkout-items.component.d.ts +1 -0
  45. package/dist/types/modules/checkout/components/summary/checkout-place-order-button.component.d.ts +0 -1
  46. package/dist/types/modules/checkout/constant.d.ts +0 -1
  47. package/dist/types/modules/product/components/index.d.ts +1 -0
  48. package/dist/types/modules/product/components/product-add-to-cart-section.component.d.ts +1 -0
  49. package/dist/types/modules/product/components/product-interactions.component.d.ts +4 -1
  50. package/dist/types/modules/product/components/product-price.component.d.ts +1 -0
  51. package/dist/types/modules/product/components/product-retailers.component.d.ts +1 -0
  52. package/dist/types/modules/product/product.commands.d.ts +1 -1
  53. package/dist/types/modules/theme-provider/services/font-manager.service.d.ts +1 -0
  54. package/dist/types/modules/ui-components/engraving/engraving-form.component.d.ts +13 -11
  55. package/dist/types/modules/ui-components/engraving/engraving-view.component.d.ts +4 -9
  56. package/dist/types/modules/ui-components/lce-element/lce-element.component.d.ts +2 -0
  57. package/dist/types/static/icon/index.d.ts +0 -1
  58. package/dist/types/utils/format.d.ts +2 -1
  59. package/docs/ACTIONS.md +1273 -0
  60. package/docs/BROWSER_SUPPORT.md +279 -0
  61. package/docs/CONFIGURATION.md +613 -0
  62. package/docs/DOCUMENTATION_INDEX.md +311 -0
  63. package/docs/EVENTS.md +765 -0
  64. package/docs/PROXY.md +228 -0
  65. package/docs/THEMING.md +592 -0
  66. package/docs/TROUBLESHOOTING.md +772 -0
  67. package/package.json +17 -17
  68. package/umd/elements.js +1 -1
  69. package/dist/types/static/icon/completed.icon.d.ts +0 -2
@@ -0,0 +1,1273 @@
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
+ **Real Business Uses:**
216
+ - **One-Click Address**: Let customers select from saved Google Places
217
+ - **Custom Address Forms**: Build your own address input forms with full control
218
+ - **Third-Party Integration**: Connect with non-Google geocoding services
219
+ - **Legacy System Migration**: Import addresses from existing customer databases
220
+ - **International Addresses**: Handle addresses that may not be in Google Places
221
+ - **Location-Based Services**: Auto-detect customer location and set address
222
+ - **Express Checkout**: Skip address forms for returning customers
223
+ - **Shipping Calculator**: Calculate shipping costs before checkout
224
+ - **Local Pickup**: Show pickup options based on their address
225
+ - **Tax Calculation**: Display accurate taxes before purchase
226
+
227
+ ## 📡 **Complete Action Feedback Events Reference**
228
+
229
+ All actions provide feedback through events. Here are all available success/failure events:
230
+
231
+ ### **Cart Action Events**
232
+ ```javascript
233
+ // Product management
234
+ 'lce:actions.cart_product_add_success' // { itemsAdded: number, identifiers: string[] }
235
+ 'lce:actions.cart_product_add_failed' // { identifiers: string[], error: string }
236
+
237
+ // Promo code management
238
+ 'lce:actions.cart_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
239
+ 'lce:actions.cart_promo_code_removed' // { applied: false }
240
+ 'lce:actions.cart_promo_code_failed' // { attempted: true, error: string }
241
+ ```
242
+
243
+ ### **Checkout Action Events**
244
+ ```javascript
245
+ // Product management
246
+ 'lce:actions.checkout_product_add_success' // { itemsAdded: number, identifiers: string[] }
247
+ 'lce:actions.checkout_product_add_failed' // { identifiers: string[], error: string }
248
+
249
+ // Promo code management
250
+ 'lce:actions.checkout_promo_code_applied' // { applied: true, discountAmount?: number, newTotal?: number }
251
+ 'lce:actions.checkout_promo_code_removed' // { applied: false }
252
+ 'lce:actions.checkout_promo_code_failed' // { attempted: true, error: string }
253
+
254
+ // Gift card management
255
+ 'lce:actions.checkout_gift_card_applied' // { applied: true, newTotal?: number }
256
+ 'lce:actions.checkout_gift_card_removed' // { applied: false }
257
+ 'lce:actions.checkout_gift_card_failed' // { attempted: true, error: string }
258
+ ```
259
+
260
+ ### **Address Action Events**
261
+ ```javascript
262
+ // Address management (already available)
263
+ 'lce:actions.address_updated' // { googlePlacesId: string, formattedAddress: string, address: {...}, coordinates: {...} }
264
+ 'lce:actions.address_failed' // { error: string, googlePlacesId?: string }
265
+ 'lce:actions.address_cleared' // boolean
266
+ ```
267
+
268
+ ### **Event Handling Best Practices**
269
+
270
+ #### **1. Set Up Listeners Before Actions**
271
+ ```javascript
272
+ // ✅ Good: Set up listener first
273
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
274
+ await actions.cart.applyPromoCode('SAVE20');
275
+
276
+ // ❌ Bad: Listener after action (might miss event)
277
+ await actions.cart.applyPromoCode('SAVE20');
278
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
279
+ ```
280
+
281
+ #### **2. Handle Both Success and Failure**
282
+ ```javascript
283
+ // ✅ Always handle both outcomes
284
+ window.addEventListener('lce:actions.cart_promo_code_applied', handleSuccess);
285
+ window.addEventListener('lce:actions.cart_promo_code_failed', handleFailure);
286
+ await actions.cart.applyPromoCode('SAVE20');
287
+ ```
288
+
289
+ #### **3. Use Event Data Effectively**
290
+ ```javascript
291
+ // ✅ Use all available event data
292
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
293
+ const { itemsAdded, identifiers } = event.detail.data;
294
+
295
+ // Show success message
296
+ showMessage(`Added ${itemsAdded} items to cart`);
297
+
298
+ // Track analytics
299
+ analytics.track('Products Added', { items: identifiers, count: itemsAdded });
300
+
301
+ // Update UI
302
+ updateCartCount();
303
+ showUpsellRecommendations(identifiers);
304
+ });
305
+ ```
306
+
307
+ #### **4. Create Reusable Event Handlers**
308
+ ```javascript
309
+ // ✅ Create reusable handlers
310
+ const handlePromoSuccess = (event) => {
311
+ const { applied, discountAmount, newTotal } = event.detail.data;
312
+ showSuccessMessage(`Discount applied! Saved: $${discountAmount}`);
313
+ updatePriceDisplay(newTotal);
314
+ trackPromoSuccess(discountAmount);
315
+ };
316
+
317
+ const handlePromoFailure = (event) => {
318
+ const { attempted, error } = event.detail.data;
319
+ showErrorMessage('Promo code could not be applied');
320
+ trackPromoFailure();
321
+ };
322
+
323
+ // Use for both cart and checkout
324
+ window.addEventListener('lce:actions.cart_promo_code_applied', handlePromoSuccess);
325
+ window.addEventListener('lce:actions.checkout_promo_code_applied', handlePromoSuccess);
326
+ window.addEventListener('lce:actions.cart_promo_code_failed', handlePromoFailure);
327
+ window.addEventListener('lce:actions.checkout_promo_code_failed', handlePromoFailure);
328
+ ```
329
+
330
+ ### 🛒 Cart Control (`actions.cart`)
331
+
332
+ **What it does**: Control the shopping cart to optimize conversions
333
+
334
+ #### Show/Hide Cart
335
+ ```javascript
336
+ // Show cart at strategic moments
337
+ actions.cart.openCart(); // Open cart
338
+ actions.cart.closeCart(); // Close cart
339
+ actions.cart.toggleCart(); // Toggle visibility
340
+
341
+ // Example: Open cart automatically after second product add
342
+ let addToCartCount = 0;
343
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
344
+ addToCartCount++;
345
+ if (addToCartCount === 2) {
346
+ actions.cart.openCart(); // "Look what you've got!"
347
+ }
348
+ });
349
+ ```
350
+
351
+ #### Programmatically Add Products to Cart
352
+ ```javascript
353
+ // Add products directly to cart without product component
354
+ await actions.cart.addProduct([
355
+ {
356
+ identifier: 'product-123', // Product UPC, ID, or Salsify grouping
357
+ fulfillmentType: 'shipping', // or 'onDemand'
358
+ quantity: 2
359
+ },
360
+ {
361
+ identifier: 'product-456',
362
+ fulfillmentType: 'onDemand',
363
+ quantity: 1
364
+ }
365
+ ], true); // Optional: open cart after adding (default: false)
366
+
367
+ // Listen for success/failure feedback
368
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
369
+ const { itemsAdded, identifiers } = event.detail.data;
370
+ console.log(`✅ Added ${itemsAdded} products:`, identifiers);
371
+ showSuccessMessage('Products added to cart!');
372
+ });
373
+
374
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
375
+ const { identifiers, error } = event.detail.data;
376
+ console.log(`❌ Failed to add products:`, identifiers, error);
377
+ showErrorMessage('Could not add products. Please try again.');
378
+ });
379
+ ```
380
+
381
+ **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.
382
+
383
+ #### Check Cart Contents
384
+ ```javascript
385
+ // See what's in the cart to make smart decisions
386
+ const cart = actions.cart.getDetails();
387
+
388
+ if (cart.amounts.total > 100) {
389
+ showFreeShippingBanner();
390
+ }
391
+
392
+ if (cart.itemCount === 0) {
393
+ showPopularProducts();
394
+ }
395
+ ```
396
+
397
+ #### Add Products to Cart with Event Feedback
398
+ ```javascript
399
+ // Add products programmatically
400
+ await actions.cart.addProduct([{
401
+ identifier: 'product-123',
402
+ fulfillmentType: 'shipping',
403
+ quantity: 1
404
+ }]);
405
+
406
+ // Listen for success
407
+ window.addEventListener('lce:actions.cart_product_add_success', function(event) {
408
+ const { itemsAdded, identifiers } = event.detail.data;
409
+ showMessage(`✅ Added ${itemsAdded} items to cart!`);
410
+
411
+ // Track conversion
412
+ analytics.track('Products Added to Cart', {
413
+ items: identifiers,
414
+ count: itemsAdded
415
+ });
416
+
417
+ // Maybe show related products
418
+ showRelatedProducts(identifiers);
419
+ });
420
+
421
+ // Listen for failures
422
+ window.addEventListener('lce:actions.cart_product_add_failed', function(event) {
423
+ const { identifiers, error } = event.detail.data;
424
+ showErrorMessage('Could not add some products. Please try again.');
425
+
426
+ // Log for debugging
427
+ console.error('Failed to add products:', identifiers, error);
428
+
429
+ // Show alternatives
430
+ showAlternativeProducts(identifiers);
431
+ });
432
+
433
+ // Add bundles or cross-sells
434
+ await actions.cart.addProduct([
435
+ { identifier: 'phone-123', fulfillmentType: 'shipping', quantity: 1 },
436
+ { identifier: 'case-456', fulfillmentType: 'shipping', quantity: 1 },
437
+ { identifier: 'charger-789', fulfillmentType: 'shipping', quantity: 1 }
438
+ ]);
439
+ ```
440
+
441
+ #### Apply Discounts with Event Feedback
442
+ ```javascript
443
+ // Apply promo codes automatically
444
+ await actions.cart.applyPromoCode('WELCOME10');
445
+
446
+ // Listen for success
447
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
448
+ const { applied, discountAmount, newTotal } = event.detail.data;
449
+ showMessage(`Welcome discount applied! You saved $${discountAmount}!`);
450
+ updateCartDisplay(newTotal);
451
+
452
+ // Track successful promo usage
453
+ analytics.track('Promo Applied', { discount: discountAmount });
454
+ });
455
+
456
+ // Listen for failure and try fallback
457
+ window.addEventListener('lce:actions.cart_promo_code_failed', function(event) {
458
+ const { attempted, error } = event.detail.data;
459
+ console.log('First promo failed, trying backup promo');
460
+
461
+ // Try another strategy
462
+ actions.cart.applyPromoCode('FIRSTTIME');
463
+ });
464
+
465
+ // Remove promo code with feedback
466
+ await actions.cart.removePromoCode();
467
+
468
+ // Listen for removal confirmation
469
+ window.addEventListener('lce:actions.cart_promo_code_removed', function(event) {
470
+ const { applied } = event.detail.data;
471
+ showMessage("Previous promo removed. Applying better discount...");
472
+
473
+ // Apply better deal
474
+ actions.cart.applyPromoCode('BETTER20');
475
+ });
476
+ ```
477
+
478
+ #### Reset Cart
479
+ ```javascript
480
+ // Clear cart for fresh start
481
+ await actions.cart.resetCart();
482
+ ```
483
+
484
+ **Real Business Uses:**
485
+ - **Smart Cart Timing**: Open cart when customer is most likely to buy
486
+ - **Bundle Sales**: Add complementary products automatically
487
+ - **Discount Strategy**: Apply best available discount automatically
488
+ - **Abandonment Prevention**: Show cart contents at right moment
489
+
490
+ ### 💳 Checkout Control (`actions.checkout`)
491
+
492
+ **What it does**: Streamline the checkout process to maximize conversions
493
+
494
+ #### Control Checkout Flow
495
+ ```javascript
496
+ // Open checkout at the perfect moment
497
+ actions.checkout.openCheckout();
498
+ actions.checkout.closeCheckout();
499
+ actions.checkout.toggleCheckout();
500
+
501
+ // Example: Open checkout automatically for high-value carts
502
+ const cart = actions.cart.getDetails();
503
+ if (cart.amounts.total > 500) {
504
+ actions.checkout.openCheckout(); // VIP checkout experience
505
+ }
506
+ ```
507
+
508
+ #### Programmatically Add Products to Checkout
509
+ ```javascript
510
+ // Add products directly to checkout (bypasses cart, goes straight to checkout)
511
+ await actions.checkout.addProduct([
512
+ {
513
+ identifier: 'product-123',
514
+ fulfillmentType: 'shipping',
515
+ quantity: 1
516
+ }
517
+ ], true); // Optional: open checkout after adding (default: false)
518
+
519
+ // Listen for success/failure feedback
520
+ window.addEventListener('lce:actions.checkout_product_add_success', function(event) {
521
+ const { itemsAdded, identifiers } = event.detail.data;
522
+ console.log(`✅ Added ${itemsAdded} products to checkout:`, identifiers);
523
+ showSuccessMessage('Ready for checkout!');
524
+ });
525
+
526
+ window.addEventListener('lce:actions.checkout_product_add_failed', function(event) {
527
+ const { identifiers, error } = event.detail.data;
528
+ console.log(`❌ Failed to add products to checkout:`, identifiers, error);
529
+ showErrorMessage('Could not proceed to checkout. Please try again.');
530
+ });
531
+ ```
532
+
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.
570
+
571
+ #### Pre-fill Customer Information
572
+ ```javascript
573
+ // Speed up checkout by pre-filling known information
574
+ actions.checkout.updateCustomerInfo({
575
+ firstName: 'John',
576
+ lastName: 'Doe',
577
+ email: 'john@example.com',
578
+ phone: '+1234567890'
579
+ });
580
+
581
+ // Pre-fill billing from your CRM
582
+ const customerData = getCRMData(customerId);
583
+ actions.checkout.updateBillingInfo({
584
+ firstName: customerData.firstName,
585
+ lastName: customerData.lastName,
586
+ street1: customerData.address,
587
+ city: customerData.city,
588
+ state: customerData.state,
589
+ zipCode: customerData.zip
590
+ });
591
+ ```
592
+
593
+ #### Smart Discount Application with Event Feedback
594
+ ```javascript
595
+ // Apply the best available discount automatically
596
+ const customer = getCurrentCustomer();
597
+
598
+ // Set up event listeners first
599
+ window.addEventListener('lce:actions.checkout_promo_code_applied', function(event) {
600
+ const { applied, discountAmount, newTotal } = event.detail.data;
601
+ showCheckoutSuccess(`Discount applied! You saved $${discountAmount}`);
602
+ updateCheckoutTotal(newTotal);
603
+
604
+ // Track successful conversion optimization
605
+ analytics.track('Checkout Discount Applied', { discount: discountAmount });
606
+ });
607
+
608
+ window.addEventListener('lce:actions.checkout_promo_code_failed', function(event) {
609
+ const { attempted, error } = event.detail.data;
610
+ console.log('Promo code failed:', error);
611
+
612
+ // Try fallback strategies or notify user
613
+ showCheckoutError('Discount could not be applied');
614
+ });
615
+
616
+ // Apply gift card event listeners
617
+ window.addEventListener('lce:actions.checkout_gift_card_applied', function(event) {
618
+ const { applied, newTotal } = event.detail.data;
619
+ showCheckoutSuccess('Gift card applied to your order!');
620
+ updateCheckoutTotal(newTotal);
621
+ });
622
+
623
+ window.addEventListener('lce:actions.checkout_gift_card_failed', function(event) {
624
+ const { attempted, error } = event.detail.data;
625
+ showCheckoutError('Gift card could not be applied. Please check and try again.');
626
+ });
627
+
628
+ // Now apply the discounts
629
+ if (customer.isFirstTime) {
630
+ await actions.checkout.applyPromoCode('WELCOME20');
631
+ } else if (customer.isVIP) {
632
+ await actions.checkout.applyPromoCode('VIP15');
633
+ }
634
+
635
+ // Apply gift cards automatically
636
+ if (customer.hasGiftCard) {
637
+ await actions.checkout.applyGiftCard(customer.giftCardCode);
638
+ }
639
+ ```
640
+
641
+ #### Optimize Checkout Options
642
+ ```javascript
643
+ // Smart defaults based on customer behavior
644
+ if (customer.prefersGifts) {
645
+ await actions.checkout.toggleIsGift(true);
646
+ }
647
+
648
+ // Auto-enable marketing for engaged customers
649
+ if (customer.engagementScore > 8) {
650
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
651
+ actions.checkout.toggleMarketingPreferences('canSms', true);
652
+ }
653
+
654
+ // Smart billing address handling
655
+ if (customer.billingAddress === customer.shippingAddress) {
656
+ await actions.checkout.toggleBillingSameAsShipping(true);
657
+ }
658
+ ```
659
+
660
+ #### Get Checkout Details
661
+ ```javascript
662
+ // Get safe, non-sensitive checkout information
663
+ const checkout = actions.checkout.getDetails();
664
+
665
+ // Make business decisions based on checkout state
666
+ if (checkout.amounts.total > 1000) {
667
+ // High-value order - offer white glove service
668
+ showWhiteGloveService();
669
+ }
670
+
671
+ if (checkout.itemCount > 5) {
672
+ // Large order - offer bulk discount
673
+ await actions.checkout.applyPromoCode('BULK15');
674
+ }
675
+
676
+ if (checkout.isGift && checkout.hasAgeVerify) {
677
+ // Gift order with age verification - show age verification requirements
678
+ showAgeVerificationNotice();
679
+ }
680
+
681
+ if (!checkout.hasPromoCode) {
682
+ // No promo code applied - suggest best available discount
683
+ suggestBestPromoCode(checkout.amounts.total);
684
+ }
685
+
686
+ if (checkout.hasGiftCards && checkout.amounts.total < 50) {
687
+ // Gift cards applied but low total - suggest adding more items
688
+ showUpsellSuggestions();
689
+ }
690
+
691
+ // Available checkout details (all non-sensitive):
692
+ // - amounts: { total, subtotal, shipping, discounts, tax, etc. }
693
+ // - items: { [itemId]: { name, price, quantity, brand, etc. } }
694
+ // - isGift: boolean, hasAgeVerify: boolean
695
+ // - hasPromoCode: boolean, hasGiftCards: boolean
696
+ // - itemCount: number
697
+ ```
698
+
699
+ **Real Business Uses:**
700
+ - **Reduce Checkout Time**: Pre-fill known customer information
701
+ - **Maximize Discounts**: Apply best available offer automatically
702
+ - **VIP Experience**: Different checkout flow for high-value customers
703
+ - **Gift Optimization**: Auto-enable gift options for gift buyers
704
+
705
+ ## Real-World Business Scenarios
706
+
707
+ ### 🎯 Scenario 1: VIP Customer Experience
708
+ ```javascript
709
+ // Detect VIP customers and give them special treatment
710
+ async function handleVIPCustomer(customer) {
711
+ if (customer.totalSpent > 5000) {
712
+ // Pre-fill everything for express checkout
713
+ actions.checkout.updateCustomerInfo(customer.profile);
714
+ actions.checkout.updateBillingInfo(customer.billingAddress);
715
+
716
+ // Apply VIP discount automatically
717
+ await actions.checkout.applyPromoCode('VIP20');
718
+
719
+ // Enable premium features
720
+ actions.checkout.toggleMarketingPreferences('canEmail', true);
721
+
722
+ // Open checkout immediately - no cart friction
723
+ actions.checkout.openCheckout();
724
+
725
+ showVIPMessage("Welcome back! Your VIP checkout is ready.");
726
+ }
727
+ }
728
+ ```
729
+
730
+ ### 🛍️ Scenario 2: Smart Bundle Sales
731
+ ```javascript
732
+ // Automatically add complementary products based on specific product identifiers
733
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
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
+ };
741
+
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
+ );
752
+
753
+ showMessage("We've added recommended accessories to your cart!");
754
+ }
755
+ });
756
+ ```
757
+
758
+ ### 📱 Scenario 3: Mobile-First Quick Checkout
759
+ ```javascript
760
+ // Streamlined mobile experience
761
+ function mobileQuickCheckout() {
762
+ if (isMobileDevice()) {
763
+ // Get customer data from their last purchase
764
+ const lastOrder = getLastOrder();
765
+
766
+ if (lastOrder) {
767
+ // Pre-fill everything
768
+ actions.checkout.updateCustomerInfo(lastOrder.customer);
769
+ actions.checkout.updateBillingInfo(lastOrder.billing);
770
+
771
+ // Skip cart, go straight to checkout
772
+ actions.checkout.openCheckout();
773
+
774
+ showMessage("Express checkout ready! Same info as last time.");
775
+ }
776
+ }
777
+ }
778
+
779
+ // Trigger on Add to Cart for mobile users
780
+ window.addEventListener('lce:actions.product_add_to_cart', function() {
781
+ if (isMobileDevice() && isReturningCustomer()) {
782
+ mobileQuickCheckout();
783
+ }
784
+ });
785
+ ```
786
+
787
+ ### 💰 Scenario 4: Dynamic Pricing & Promotions
788
+ ```javascript
789
+ // Apply the best available discount
790
+ async function optimizeCheckout() {
791
+ const cart = actions.cart.getDetails();
792
+ const customer = getCurrentCustomer();
793
+
794
+ // Try different discount strategies
795
+ const discountStrategies = [
796
+ { code: 'BULK25', minTotal: 200 },
797
+ { code: 'RETURNING15', condition: customer.orderCount > 1 },
798
+ { code: 'FIRSTTIME10', condition: customer.orderCount === 0 },
799
+ { code: 'SEASONAL20', condition: isHolidaySeason() }
800
+ ];
801
+
802
+ for (const strategy of discountStrategies) {
803
+ if (strategy.minTotal && cart.amounts.total >= strategy.minTotal) {
804
+ try {
805
+ await actions.checkout.applyPromoCode(strategy.code);
806
+ break; // Success, stop trying
807
+ } catch (error) {
808
+ continue; // Try next strategy
809
+ }
810
+ } else if (strategy.condition) {
811
+ try {
812
+ await actions.checkout.applyPromoCode(strategy.code);
813
+ break;
814
+ } catch (error) {
815
+ continue;
816
+ }
817
+ }
818
+ }
819
+ }
820
+ ```
821
+
822
+ ### 🎁 Scenario 5: Holiday Gift Flow
823
+ ```javascript
824
+ // Optimize for gift purchases during holidays
825
+ async function holidayGiftOptimization() {
826
+ if (isHolidaySeason()) {
827
+ // Auto-enable gift features
828
+ actions.checkout.toggleIsGift(true);
829
+
830
+ // Pre-populate gift message templates
831
+ const giftMessages = [
832
+ "Happy Holidays!",
833
+ "Merry Christmas!",
834
+ "With love and best wishes"
835
+ ];
836
+
837
+ showGiftMessageSuggestions(giftMessages);
838
+
839
+ // Suggest gift wrapping
840
+ showGiftWrappingOption();
841
+
842
+ // Apply holiday shipping discount
843
+ await actions.checkout.applyPromoCode('FREEHOLIDAYSHIP');
844
+ }
845
+ }
846
+
847
+ // Trigger during checkout
848
+ window.addEventListener('lce:actions.checkout_opened', holidayGiftOptimization);
849
+ ```
850
+
851
+ ### 📍 Scenario 6: Smart Address Auto-Complete
852
+ ```javascript
853
+ // Use Google Places to streamline address entry
854
+ async function smartAddressFlow() {
855
+ const customer = getCurrentCustomer();
856
+
857
+ // For returning customers, use their recent addresses
858
+ if (customer.recentPlacesIds && customer.recentPlacesIds.length > 0) {
859
+ try {
860
+ // Auto-set their most recent address
861
+ await actions.address.setAddressByPlacesId(customer.recentPlacesIds[0]);
862
+
863
+ showMessage("We've set your address to your recent location. Change it if needed!");
864
+
865
+ // Show other recent addresses as quick options
866
+ showRecentAddressOptions(customer.recentPlacesIds.slice(1, 4));
867
+
868
+ } catch (error) {
869
+ // Fallback to manual entry
870
+ showAddressForm();
871
+ }
872
+ }
873
+
874
+ // For location-aware features
875
+ if (customer.hasLocationPermission) {
876
+ const nearbyBusinesses = await getNearbyBusinessPlaces();
877
+
878
+ // If they're near a business location, suggest it
879
+ if (nearbyBusinesses.length > 0) {
880
+ showMessage("We found you're near our store location. Use it for pickup?");
881
+
882
+ document.getElementById('use-store-address').onclick = async () => {
883
+ await actions.address.setAddressByPlacesId(nearbyBusinesses[0].placeId);
884
+ showMessage("Store pickup address set!");
885
+ };
886
+ }
887
+ }
888
+ }
889
+
890
+ // Use when address is updated
891
+ smartAddressFlow();
892
+
893
+ // Quick address selection from saved places
894
+ async function useQuickAddress(placeId, displayName) {
895
+ try {
896
+ await actions.address.setAddressByPlacesId(placeId);
897
+ showMessage(`Address set to ${displayName}`);
898
+
899
+ // Automatically proceed to shipping options
900
+ showShippingOptionsForAddress();
901
+
902
+ } catch (error) {
903
+ showMessage("That address is no longer available. Please select another.");
904
+ }
905
+ }
906
+ ```
907
+
908
+ ### 🏢 Scenario 7: Custom Address Form Integration
909
+ ```javascript
910
+ // Integrate with your own address forms and geocoding services
911
+ async function customAddressFormFlow() {
912
+ const customer = getCurrentCustomer();
913
+
914
+ // If customer has saved addresses in your database (not Google Places)
915
+ if (customer.savedAddresses && customer.savedAddresses.length > 0) {
916
+ showSavedAddressOptions(customer.savedAddresses);
917
+ }
918
+
919
+ // Handle custom form submission
920
+ document.getElementById('custom-address-form').addEventListener('submit', async (event) => {
921
+ event.preventDefault();
922
+
923
+ const formData = new FormData(event.target);
924
+ const addressData = {
925
+ one: formData.get('street'),
926
+ two: formData.get('apartment') || '',
927
+ city: formData.get('city'),
928
+ state: formData.get('state'),
929
+ zip: formData.get('zip'),
930
+ country: formData.get('country') || 'United States'
931
+ };
932
+
933
+ try {
934
+ // Use your geocoding service to get coordinates
935
+ const coordinates = await yourGeocodingService.geocode(
936
+ `${addressData.one}, ${addressData.city}, ${addressData.state} ${addressData.zip}`
937
+ );
938
+
939
+ // Set the address manually
940
+ await actions.address.setAddressManually(addressData, {
941
+ lat: coordinates.latitude,
942
+ long: coordinates.longitude
943
+ });
944
+
945
+ showMessage("Your address has been set successfully!");
946
+
947
+ // Save to your database for future use
948
+ saveCustomerAddress(customer.id, addressData, coordinates);
949
+
950
+ } catch (geocodeError) {
951
+ showError("Could not find coordinates for this address. Please verify and try again.");
952
+ }
953
+ });
954
+
955
+ // Handle saved address selection
956
+ async function useSavedAddress(savedAddress) {
957
+ try {
958
+ await actions.address.setAddressManually(
959
+ {
960
+ one: savedAddress.street,
961
+ two: savedAddress.apartment || '',
962
+ city: savedAddress.city,
963
+ state: savedAddress.state,
964
+ zip: savedAddress.zip,
965
+ country: savedAddress.country || 'United States'
966
+ },
967
+ {
968
+ lat: savedAddress.coordinates.lat,
969
+ long: savedAddress.coordinates.long
970
+ }
971
+ );
972
+
973
+ showMessage(`Address set to your saved location: ${savedAddress.nickname}`);
974
+
975
+ } catch (error) {
976
+ showError("There was an issue with your saved address. Please try entering it again.");
977
+ }
978
+ }
979
+ }
980
+
981
+ // Import addresses from legacy system
982
+ async function importLegacyAddresses() {
983
+ const legacyAddresses = await getLegacyCustomerAddresses();
984
+
985
+ for (const address of legacyAddresses) {
986
+ try {
987
+ // Convert legacy format to Elements format
988
+ const elementsAddress = {
989
+ one: address.address_line_1,
990
+ two: address.address_line_2 || '',
991
+ city: address.city_name,
992
+ state: address.state_code,
993
+ zip: address.postal_code,
994
+ country: address.country_name || 'United States'
995
+ };
996
+
997
+ const coordinates = {
998
+ lat: address.latitude,
999
+ long: address.longitude
1000
+ };
1001
+
1002
+ // Set address in Elements system
1003
+ await actions.address.setAddressManually(elementsAddress, coordinates);
1004
+
1005
+ console.log(`Successfully imported address for customer ${address.customer_id}`);
1006
+
1007
+ } catch (error) {
1008
+ console.error(`Failed to import address for customer ${address.customer_id}:`, error);
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ // Handle international addresses that might not be in Google Places
1014
+ async function handleInternationalAddress(internationalAddressData) {
1015
+ try {
1016
+ // Use an international geocoding service
1017
+ const coordinates = await internationalGeocodingService.geocode(
1018
+ internationalAddressData.fullAddress,
1019
+ internationalAddressData.country
1020
+ );
1021
+
1022
+ await actions.address.setAddressManually(
1023
+ {
1024
+ one: internationalAddressData.addressLine1,
1025
+ two: internationalAddressData.addressLine2 || '',
1026
+ city: internationalAddressData.city,
1027
+ state: internationalAddressData.region, // Province, state, etc.
1028
+ zip: internationalAddressData.postalCode,
1029
+ country: internationalAddressData.country
1030
+ },
1031
+ coordinates
1032
+ );
1033
+
1034
+ showMessage("International address set successfully!");
1035
+
1036
+ } catch (error) {
1037
+ showError("Could not process this international address. Please contact support.");
1038
+ }
1039
+ }
1040
+ ```
1041
+
1042
+ ## Implementation Strategy
1043
+
1044
+ ### 🚀 Quick Start (5-Minute Setup)
1045
+
1046
+ **Step 1: Choose Your Goal**
1047
+ - Increase conversions? → Focus on cart and checkout actions
1048
+ - Reduce abandonment? → Use pre-filling and automation
1049
+ - Boost sales? → Implement bundle and upsell logic
1050
+ - Improve UX? → Create smart defaults and express flows
1051
+
1052
+ **Step 2: Start Simple**
1053
+ ```javascript
1054
+ // Copy this basic template and customize
1055
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1056
+ const product = event.detail.data;
1057
+
1058
+ // Your business logic here
1059
+ if (product.priceInfo && product.priceInfo.avg > 100) {
1060
+ // High-value product - offer premium support
1061
+ showPremiumSupportOffer();
1062
+ }
1063
+
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]) {
1071
+ await actions.cart.addProduct([{
1072
+ identifier: crossSellMap[product.identifier],
1073
+ fulfillmentType: 'shipping',
1074
+ quantity: 1
1075
+ }]);
1076
+ }
1077
+ });
1078
+ ```
1079
+
1080
+ **Step 3: Test & Iterate**
1081
+ 1. Add one action at a time
1082
+ 2. Test with real user flows
1083
+ 3. Monitor conversion impact
1084
+ 4. Adjust based on results
1085
+
1086
+ ### 💡 Pro Implementation Tips
1087
+
1088
+ #### Combine Actions with Events
1089
+ ```javascript
1090
+ // Listen for events, respond with actions
1091
+ window.addEventListener('lce:actions.cart_opened', function() {
1092
+ const cart = actions.cart.getDetails();
1093
+
1094
+ // If cart value is close to free shipping threshold
1095
+ if (cart.amounts.total > 45 && cart.amounts.total < 50) {
1096
+ showFreeShippingUpsell();
1097
+ }
1098
+ });
1099
+
1100
+ // Pre-fill checkout for VIP customers
1101
+ window.addEventListener('lce:actions.checkout_opened', async function() {
1102
+ const customer = getCurrentCustomer();
1103
+
1104
+ if (customer.isVIP) {
1105
+ actions.checkout.updateCustomerInfo(customer.profile);
1106
+ await actions.checkout.applyPromoCode('VIP20');
1107
+ }
1108
+ });
1109
+ ```
1110
+
1111
+ #### Chain Actions for Workflows
1112
+ ```javascript
1113
+ // Create complete automated flows
1114
+ async function expressCheckoutFlow(productId, customerId) {
1115
+ // 1. Add product to cart
1116
+ await actions.cart.addProduct([{
1117
+ identifier: productId,
1118
+ fulfillmentType: 'shipping',
1119
+ quantity: 1
1120
+ }]);
1121
+
1122
+ // 2. Pre-fill customer data
1123
+ const customer = getCustomerData(customerId);
1124
+ actions.checkout.updateCustomerInfo(customer);
1125
+
1126
+ // 3. Apply best discount
1127
+ await actions.checkout.applyPromoCode(getBestDiscountFor(customer));
1128
+
1129
+ // 4. Open checkout
1130
+ actions.checkout.openCheckout();
1131
+
1132
+ showMessage("Express checkout ready! Review and complete your order.");
1133
+ }
1134
+ ```
1135
+
1136
+ #### Smart Error Handling
1137
+ ```javascript
1138
+ // Graceful degradation
1139
+ async function smartAddToCart(productId) {
1140
+ try {
1141
+ await actions.cart.addProduct([{
1142
+ identifier: productId,
1143
+ fulfillmentType: 'shipping',
1144
+ quantity: 1
1145
+ }]);
1146
+
1147
+ showSuccessMessage("Added to cart!");
1148
+ } catch (error) {
1149
+ // Fallback - maybe the product is out of stock
1150
+ showMessage("This item isn't available, but here are similar options:");
1151
+ showAlternativeProducts(productId);
1152
+ }
1153
+ }
1154
+ ```
1155
+
1156
+ ## Common Integration Patterns
1157
+
1158
+ ### 🎯 **Pattern 1: Behavioral Triggers**
1159
+ ```javascript
1160
+ // Trigger actions based on customer behavior
1161
+ let pageViewCount = 0;
1162
+ let timeOnPage = 0;
1163
+
1164
+ // Track engagement
1165
+ setInterval(() => timeOnPage++, 1000);
1166
+
1167
+ // Act based on engagement
1168
+ if (timeOnPage > 60 && pageViewCount > 3) {
1169
+ // Engaged user - show special offer
1170
+ await actions.cart.applyPromoCode('ENGAGED15');
1171
+ showSpecialOffer();
1172
+ }
1173
+ ```
1174
+
1175
+ ### 🛍️ **Pattern 2: Cross-Sell Automation**
1176
+ ```javascript
1177
+ // Automatic product recommendations based on identifier
1178
+ const crossSellRules = {
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']
1182
+ };
1183
+
1184
+ window.addEventListener('lce:actions.product_add_to_cart', async function(event) {
1185
+ const data = event.detail.data;
1186
+ const recommendations = crossSellRules[data.identifier];
1187
+
1188
+ if (recommendations) {
1189
+ // Add first recommendation automatically
1190
+ await actions.cart.addProduct([{
1191
+ identifier: recommendations[0],
1192
+ fulfillmentType: 'shipping',
1193
+ quantity: 1
1194
+ }]);
1195
+
1196
+ showMessage(`We've added ${recommendations[0]} - customers love this combo!`);
1197
+ }
1198
+ });
1199
+ ```
1200
+
1201
+ ### 💰 **Pattern 3: Dynamic Pricing**
1202
+ ```javascript
1203
+ // Apply best available discount automatically
1204
+ async function optimizePricing() {
1205
+ const cart = actions.cart.getDetails();
1206
+ const customer = getCurrentCustomer();
1207
+ const now = new Date();
1208
+
1209
+ // Time-based discounts
1210
+ if (now.getHours() < 10) {
1211
+ await actions.cart.applyPromoCode('EARLYBIRD10');
1212
+ }
1213
+
1214
+ // Volume discounts
1215
+ else if (cart.amounts.total > 200) {
1216
+ await actions.cart.applyPromoCode('BULK20');
1217
+ }
1218
+
1219
+ // Customer-specific discounts
1220
+ else if (customer.orderCount === 0) {
1221
+ await actions.cart.applyPromoCode('WELCOME15');
1222
+ }
1223
+
1224
+ // Seasonal discounts
1225
+ else if (isBlackFriday()) {
1226
+ await actions.cart.applyPromoCode('BLACKFRIDAY25');
1227
+ }
1228
+ }
1229
+ ```
1230
+
1231
+ ## Business ROI Tracking
1232
+
1233
+ Track the impact of your action implementations:
1234
+
1235
+ ```javascript
1236
+ // Track business metrics
1237
+ const metrics = {
1238
+ automatedDiscountsApplied: 0,
1239
+ bundleSalesCreated: 0,
1240
+ expressCheckoutsCompleted: 0,
1241
+ cartAbandonmentsPrevented: 0
1242
+ };
1243
+
1244
+ // Track when actions create business value
1245
+ window.addEventListener('lce:actions.cart_updated', function() {
1246
+ if (wasDiscountAppliedAutomatically()) {
1247
+ metrics.automatedDiscountsApplied++;
1248
+ }
1249
+ });
1250
+
1251
+ window.addEventListener('lce:actions.checkout_submit_completed', function() {
1252
+ if (wasExpressCheckout()) {
1253
+ metrics.expressCheckoutsCompleted++;
1254
+ }
1255
+ });
1256
+
1257
+ // Send metrics to your analytics
1258
+ setInterval(() => {
1259
+ sendBusinessMetrics(metrics);
1260
+ }, 60000); // Every minute
1261
+ ```
1262
+
1263
+ ## Getting Started Today
1264
+
1265
+ 1. **Pick ONE scenario** from the examples above that matches your biggest business need
1266
+ 2. **Copy the code** and customize it for your products/customers
1267
+ 3. **Test it** with a small group first
1268
+ 4. **Measure the impact** on your conversion rates
1269
+ 5. **Scale up** what works, iterate on what doesn't
1270
+
1271
+ **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!
1272
+
1273
+ Need help implementing? The actions work automatically once LiquidCommerce Elements are installed - just add your business logic and watch conversions improve!