@liquidcommerce/elements-sdk 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/EVENTS.md ADDED
@@ -0,0 +1,765 @@
1
+ # Elements SDK Events - Partner Implementation 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 Events and Why Use Them?
6
+
7
+ Events are real-time notifications that tell you exactly what your customers are doing on your site. Think of them as your digital sales assistant that whispers in your ear every time a customer looks at a product, adds something to cart, or starts checkout.
8
+
9
+ **Business Benefits:**
10
+ - **Track Revenue Impact**: See which products drive the most sales
11
+ - **Optimize Conversion**: Identify where customers drop off
12
+ - **Personalize Experience**: Trigger custom actions based on customer behavior
13
+ - **Measure Performance**: Get real metrics on your e-commerce funnel
14
+ - **Automate Marketing**: Send targeted emails/SMS based on customer actions
15
+
16
+ ## How It Works
17
+
18
+ When customers interact with your LiquidCommerce elements, events automatically fire. You simply "listen" for these events and take action:
19
+
20
+ ```javascript
21
+ // Example: When someone adds to cart, send them a discount code
22
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
23
+ // Customer just added a product to cart!
24
+ const data = event.detail.data;
25
+
26
+ // Maybe send them a discount popup
27
+ showDiscountPopup(data.identifier);
28
+
29
+ // Or track in your analytics
30
+ analytics.track('Product Added', {
31
+ identifier: data.identifier,
32
+ quantity: data.quantity,
33
+ upc: data.upc
34
+ });
35
+ });
36
+ ```
37
+
38
+ ## Quick Implementation Guide
39
+
40
+ ### Step 1: Choose Your Goal
41
+ What do you want to achieve? Pick one:
42
+
43
+ **🎯 Track Sales Performance**
44
+ ```javascript
45
+ // Track when products are viewed and added to cart
46
+ window.addEventListener('lce:actions.product_loaded', function(event) {
47
+ // Product was viewed - track in your analytics
48
+ const product = event.detail.data;
49
+ console.log('Customer viewed:', product.name);
50
+ });
51
+
52
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
53
+ // Product was added to cart - this is a conversion!
54
+ const data = event.detail.data;
55
+ console.log('SALE OPPORTUNITY:', data.identifier, 'qty:', data.quantity);
56
+ });
57
+ ```
58
+
59
+ **🛒 Recover Abandoned Carts**
60
+ ```javascript
61
+ // Detect when someone opens cart but doesn't buy
62
+ window.addEventListener('lce:actions.cart_opened', function(event) {
63
+ // Start a timer - if they don't complete purchase in 10 minutes, send email
64
+ setTimeout(function() {
65
+ sendAbandonedCartEmail();
66
+ }, 10 * 60 * 1000); // 10 minutes
67
+ });
68
+
69
+ window.addEventListener('lce:actions.checkout_submit_completed', function(event) {
70
+ // They bought! Cancel the abandoned cart email
71
+ cancelAbandonedCartEmail();
72
+ });
73
+ ```
74
+
75
+ **💰 Maximize Revenue**
76
+ ```javascript
77
+ // Offer upsells when they add expensive items
78
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
79
+ const data = event.detail.data;
80
+
81
+ // Trigger upsell based on product identifier
82
+ showUpsellPopup(data.identifier);
83
+ });
84
+ ```
85
+
86
+ ## Step 2: Complete Event List & What They Mean
87
+
88
+ ### 🚀 SDK Lifecycle Events
89
+ ```javascript
90
+ // When SDK is ready to use
91
+ 'lce:actions.client_ready' // → "SDK is initialized and ready to use"
92
+ ```
93
+
94
+ **Example Usage:**
95
+ ```javascript
96
+ window.addEventListener('lce:actions.client_ready', function(event) {
97
+ const { isReady, message, timestamp, version } = event.detail.data;
98
+ console.log('SDK Ready:', version);
99
+ // Now safe to call SDK methods
100
+ });
101
+ ```
102
+
103
+ ### 🛍️ Product Events (Track Customer Interest)
104
+ ```javascript
105
+ // When a customer views a product
106
+ 'lce:actions.product_loaded' // → "Customer is interested in this product"
107
+
108
+ // When they add to cart
109
+ 'lce:actions.product_add_to_cart' // → "Hot lead! Customer wants to buy"
110
+
111
+ // When they change quantity
112
+ 'lce:actions.product_quantity_increase' // → "Customer wants more"
113
+ 'lce:actions.product_quantity_decrease' // → "Customer wants less"
114
+
115
+ // When they change options
116
+ 'lce:actions.product_size_changed' // → "Customer has size preferences"
117
+ 'lce:actions.product_fulfillment_type_changed' // → "Delivery preference changed"
118
+ 'lce:actions.product_fulfillment_changed' // → "Customer selected specific retailer/fulfillment"
119
+ ```
120
+
121
+ **Note on Personalization:** Product personalization (engraving) is added during the add-to-cart action. The personalization is included in the `product_add_to_cart` event data. Once in cart, use `cart_item_engraving_updated` to track changes.
122
+
123
+ #### Product Details Event Data
124
+ When `lce:actions.product_loaded` fires, you get comprehensive product information:
125
+
126
+ ```javascript
127
+ window.addEventListener('lce:actions.product_loaded', function(event) {
128
+ const product = event.detail.data;
129
+
130
+ // Basic Information
131
+ console.log(product.id); // Product ID
132
+ console.log(product.name); // Product name
133
+ console.log(product.brand); // Brand name
134
+ console.log(product.category); // Category
135
+ console.log(product.catPath); // Category path
136
+ console.log(product.classification); // Product classification
137
+ console.log(product.type); // Product type
138
+ console.log(product.subType); // Product sub-type
139
+ console.log(product.salsifyGrouping); // Salsify grouping ID
140
+
141
+ // Visual Assets
142
+ console.log(product.mainImage); // Main product image URL
143
+ console.log(product.images); // Array of all product images
144
+
145
+ // Product Attributes
146
+ console.log(product.region); // Region (e.g., "Napa Valley")
147
+ console.log(product.country); // Country of origin
148
+ console.log(product.material); // Material type
149
+ console.log(product.abv); // Alcohol by volume
150
+ console.log(product.proof); // Proof
151
+ console.log(product.age); // Age statement
152
+ console.log(product.color); // Color description
153
+ console.log(product.flavor); // Flavor profile
154
+ console.log(product.variety); // Variety (e.g., "Cabernet Sauvignon")
155
+ console.log(product.appellation); // Appellation
156
+ console.log(product.vintage); // Vintage year
157
+
158
+ // Descriptions
159
+ console.log(product.description); // Plain text description
160
+ console.log(product.htmlDescription); // HTML formatted description
161
+ console.log(product.tastingNotes); // Tasting notes
162
+
163
+ // Pricing
164
+ console.log(product.priceInfo); // { min, max, avg } pricing across fulfillments
165
+
166
+ // Available Sizes & Fulfillments
167
+ console.log(product.sizes); // All available sizes with fulfillment options
168
+ console.log(product.selectedSizeId); // Currently selected size
169
+ console.log(product.selectedFulfillmentId); // Currently selected fulfillment
170
+ console.log(product.selectedFulfillmentType); // 'shipping' or 'onDemand'
171
+ });
172
+ ```
173
+
174
+ **Use Cases:**
175
+ - **Rich Analytics**: Track which product attributes drive conversions
176
+ - **Personalized Recommendations**: Suggest similar products based on variety, region, or flavor
177
+ - **Content Display**: Show detailed product information on your custom UI
178
+ - **Search & Filtering**: Build custom search based on product attributes
179
+
180
+ ### 🛒 Cart Events (Track Purchase Intent)
181
+ ```javascript
182
+ // Cart lifecycle
183
+ 'lce:actions.cart_loaded' // → "Cart data has been loaded and is ready"
184
+ 'lce:actions.cart_opened' // → "Customer is reviewing their purchase"
185
+ 'lce:actions.cart_closed' // → "Customer might be comparison shopping"
186
+
187
+ // Cart changes
188
+ 'lce:actions.cart_updated' // → "Customer modified their order"
189
+ 'lce:actions.cart_reset' // → "Customer cleared everything - intervention needed!"
190
+ 'lce:actions.cart_failed' // → "Cart operation failed"
191
+
192
+ // Item management
193
+ 'lce:actions.cart_item_added' // → "Customer committed to another product"
194
+ 'lce:actions.cart_item_removed' // → "Customer changed their mind"
195
+ 'lce:actions.cart_item_quantity_increase' // → "Customer added more of an item"
196
+ 'lce:actions.cart_item_quantity_decrease' // → "Customer reduced item quantity"
197
+ 'lce:actions.cart_item_engraving_updated' // → "Customer updated/removed item personalization"
198
+ ```
199
+
200
+ **Personalization Events:**
201
+ ```javascript
202
+ // Track when customers add or modify personalization
203
+ window.addEventListener('lce:actions.cart_item_engraving_updated', function(event) {
204
+ const { identifier, engravingLines, hasEngraving } = event.detail.data;
205
+
206
+ if (hasEngraving && engravingLines.length > 0) {
207
+ console.log(`Personalization added/updated: ${engravingLines.join(', ')}`);
208
+ } else {
209
+ console.log('Personalization removed from item');
210
+ }
211
+ });
212
+ ```
213
+
214
+ #### Cart Details Event Data
215
+ When `lce:actions.cart_loaded` or `lce:actions.cart_updated` fires, you get complete cart information:
216
+
217
+ ```javascript
218
+ window.addEventListener('lce:actions.cart_loaded', function(event) {
219
+ const cart = event.detail.data;
220
+
221
+ // Cart Identification
222
+ console.log(cart.id); // Cart ID
223
+ console.log(cart.promoCode); // Applied promo code (if any)
224
+
225
+ // Cart Items
226
+ console.log(cart.items); // Object with all cart items
227
+ console.log(cart.itemCount); // Total number of items in cart
228
+
229
+ // Each item contains:
230
+ Object.values(cart.items).forEach(item => {
231
+ console.log(item.id); // Cart item ID
232
+ console.log(item.name); // Product name
233
+ console.log(item.brand); // Brand
234
+ console.log(item.salsifyGrouping); // Salsify grouping ID
235
+ console.log(item.catPath); // Category path
236
+ console.log(item.size); // Size description
237
+ console.log(item.volume); // Volume
238
+ console.log(item.uom); // Unit of measure
239
+ console.log(item.pack); // Is pack (boolean)
240
+ console.log(item.packDesc); // Pack description
241
+ console.log(item.container); // Container type
242
+ console.log(item.containerType); // Container type classification
243
+ console.log(item.price); // Total price (unitPrice × quantity)
244
+ console.log(item.unitPrice); // Price per unit
245
+ console.log(item.quantity); // Quantity
246
+ console.log(item.maxQuantity); // Maximum allowed quantity
247
+ console.log(item.mainImage); // Product image URL
248
+ console.log(item.upc); // UPC code
249
+ console.log(item.sku); // SKU
250
+ console.log(item.partNumber); // Part number
251
+ console.log(item.retailerId); // Retailer ID
252
+ console.log(item.fulfillmentId); // Fulfillment ID
253
+ console.log(item.attributes); // Item attributes (engraving, etc.)
254
+ });
255
+
256
+ // Location Information
257
+ console.log(cart.location.placesId); // Google Places ID
258
+ console.log(cart.location.address); // Address object
259
+ console.log(cart.location.coordinates); // Lat/long coordinates
260
+
261
+ // Financial Breakdown
262
+ console.log(cart.amounts.subtotal); // Subtotal
263
+ console.log(cart.amounts.deliveryFee); // Delivery fee
264
+ console.log(cart.amounts.shippingFee); // Shipping fee
265
+ console.log(cart.amounts.platformFee); // Platform fee
266
+ console.log(cart.amounts.engravingFee); // Engraving/personalization fee
267
+ console.log(cart.amounts.discounts); // Total discounts
268
+ console.log(cart.amounts.giftCardTotal); // Gift card total applied
269
+ console.log(cart.amounts.total); // Final total
270
+
271
+ // Timestamps
272
+ console.log(cart.createdAt); // When cart was created
273
+ console.log(cart.updatedAt); // Last update timestamp
274
+ });
275
+ ```
276
+
277
+ **Use Cases:**
278
+ - **Abandoned Cart Recovery**: Track cart value and items for targeted recovery campaigns
279
+ - **Revenue Analytics**: Monitor average cart value, most popular items, cart composition
280
+ - **Inventory Management**: Track which products are frequently added/removed
281
+ - **Pricing Analysis**: Analyze impact of fees, discounts, and gift cards on cart totals
282
+
283
+ ### 💳 Checkout Events (Track Purchase Process)
284
+ ```javascript
285
+ // Checkout lifecycle
286
+ 'lce:actions.checkout_loaded' // → "Checkout data has been loaded and is ready"
287
+ 'lce:actions.checkout_opened' // → "Customer is ready to buy!"
288
+ 'lce:actions.checkout_closed' // → "Customer left checkout"
289
+ 'lce:actions.checkout_failed' // → "Checkout operation failed"
290
+
291
+ // Checkout submission
292
+ 'lce:actions.checkout_submit_started' // → "Payment processing..."
293
+ 'lce:actions.checkout_submit_completed' // → "SALE COMPLETED! 🎉"
294
+ 'lce:actions.checkout_submit_failed' // → "Sale lost - fix the issue!"
295
+
296
+ // Form updates
297
+ 'lce:actions.checkout_customer_information_updated' // → "Customer filling out details"
298
+ 'lce:actions.checkout_billing_information_updated' // → "Payment info being entered"
299
+ 'lce:actions.checkout_gift_information_updated' // → "Gift recipient info updated"
300
+
301
+ // Checkout options
302
+ 'lce:actions.checkout_is_gift_toggled' // → "Customer toggled gift option"
303
+ 'lce:actions.checkout_billing_same_as_shipping_toggled' // → "Customer toggled billing address option"
304
+ 'lce:actions.checkout_marketing_preferences_toggled' // → "Customer updated marketing preferences"
305
+
306
+ // Item management in checkout
307
+ 'lce:actions.checkout_item_removed' // → "Customer removed item from checkout"
308
+ 'lce:actions.checkout_item_quantity_increase' // → "Customer increased item quantity in checkout"
309
+ 'lce:actions.checkout_item_quantity_decrease' // → "Customer decreased item quantity in checkout"
310
+ 'lce:actions.checkout_item_engraving_updated' // → "Customer removed personalization in checkout"
311
+
312
+ // Tips (for on-demand delivery)
313
+ 'lce:actions.checkout_tip_updated' // → "Customer updated delivery tip"
314
+ ```
315
+
316
+ **Important Note on Checkout Personalization:**
317
+ Personalization cannot be edited during checkout - only removed. This is by design to prevent order processing complications. If a customer wants to modify personalization, they must return to the cart. The checkout only displays personalization details and provides a remove option.
318
+
319
+ #### Checkout Details Event Data
320
+ When you call `actions.checkout.getDetails()`, you get comprehensive checkout information:
321
+
322
+ **Note:** The `lce:actions.checkout_loaded` event only contains `{ cartId: string }`. To get full checkout details, use `actions.checkout.getDetails()`.
323
+
324
+ ```javascript
325
+ // Get full checkout details via action
326
+ const checkout = actions.checkout.getDetails();
327
+
328
+ // Checkout Identification
329
+ console.log(checkout.cartId); // Associated cart ID
330
+
331
+ // Checkout Status Flags
332
+ console.log(checkout.acceptedAccountCreation); // Customer accepted account creation
333
+ console.log(checkout.hasAgeVerify); // Age verification required
334
+ console.log(checkout.hasSubstitutionPolicy); // Substitution policy accepted
335
+ console.log(checkout.isGift); // Order is a gift
336
+ console.log(checkout.billingSameAsShipping); // Billing address same as shipping
337
+ console.log(checkout.hasPromoCode); // Promo code applied
338
+ console.log(checkout.hasGiftCards); // Gift cards applied
339
+
340
+ // Marketing Preferences
341
+ console.log(checkout.marketingPreferences.canEmail); // Email opt-in status
342
+ console.log(checkout.marketingPreferences.canSms); // SMS opt-in status
343
+
344
+ // Order Summary
345
+ console.log(checkout.itemCount); // Total items in checkout
346
+
347
+ // Financial Breakdown
348
+ console.log(checkout.amounts.subtotal); // Subtotal
349
+ console.log(checkout.amounts.deliveryFee); // Delivery fee
350
+ console.log(checkout.amounts.shippingFee); // Shipping fee
351
+ console.log(checkout.amounts.platformFee); // Platform fee
352
+ console.log(checkout.amounts.engravingFee); // Engraving/personalization fee
353
+ console.log(checkout.amounts.discounts); // Total discounts
354
+ console.log(checkout.amounts.giftCardTotal); // Gift card total applied
355
+ console.log(checkout.amounts.tax); // Tax amount
356
+ console.log(checkout.amounts.bottleDeposits); // Bottle deposit fees
357
+ console.log(checkout.amounts.total); // Final total
358
+
359
+ // Checkout Items (detailed product information)
360
+ console.log(checkout.items); // Object with all checkout items
361
+
362
+ // Each checkout item contains comprehensive details:
363
+ Object.values(checkout.items).forEach(item => {
364
+ console.log(item.cartItemId); // Cart item ID
365
+ console.log(item.liquidId); // Liquid ID
366
+ console.log(item.variantId); // Variant ID
367
+ console.log(item.name); // Product name
368
+ console.log(item.brand); // Brand
369
+ console.log(item.salsifyGrouping); // Salsify grouping ID
370
+ console.log(item.catPath); // Category path
371
+
372
+ // Size & Container Information
373
+ console.log(item.size); // Size description
374
+ console.log(item.volume); // Volume
375
+ console.log(item.uom); // Unit of measure
376
+ console.log(item.pack); // Is pack (boolean)
377
+ console.log(item.packDesc); // Pack description
378
+ console.log(item.container); // Container type
379
+ console.log(item.containerType); // Container type classification
380
+
381
+ // Product Attributes
382
+ console.log(item.proof); // Proof
383
+ console.log(item.abv); // Alcohol by volume
384
+
385
+ // Pricing
386
+ console.log(item.price); // Total price (unitPrice × quantity)
387
+ console.log(item.unitPrice); // Price per unit
388
+ console.log(item.unitTax); // Tax per unit
389
+ console.log(item.bottleDeposits); // Bottle deposit fees
390
+ console.log(item.quantity); // Quantity
391
+
392
+ // Identifiers
393
+ console.log(item.upc); // UPC code
394
+ console.log(item.sku); // SKU
395
+ console.log(item.partNumber); // Part number
396
+
397
+ // Fulfillment Information
398
+ console.log(item.retailerId); // Retailer ID
399
+ console.log(item.retailerName); // Retailer name
400
+ console.log(item.fulfillmentId); // Fulfillment ID
401
+ console.log(item.expectationDetail); // Delivery expectation details
402
+
403
+ // Visual Assets
404
+ console.log(item.mainImage); // Product image URL
405
+
406
+ // Item Attributes (engraving, etc.)
407
+ console.log(item.attributes); // Item attributes object
408
+ });
409
+ ```
410
+
411
+ **Use Cases:**
412
+ - **Order Analytics**: Track checkout completion rates, average order values, popular items
413
+ - **Marketing Optimization**: Analyze opt-in rates, gift order patterns, promo code effectiveness
414
+ - **Revenue Tracking**: Monitor tax collection, fees, discounts, and gift card usage
415
+ - **Customer Insights**: Understand purchasing patterns, preferred retailers, delivery preferences
416
+ - **Compliance**: Track age verification requirements, substitution policy acceptance
417
+
418
+ ### 📍 Address Events (Track Shipping Preferences)
419
+ ```javascript
420
+ 'lce:actions.address_updated' // → "Customer entered shipping address"
421
+ 'lce:actions.address_cleared' // → "Customer removed address"
422
+ 'lce:actions.address_failed' // → "Address validation failed"
423
+ ```
424
+
425
+ ### 🔔 **NEW: Action Feedback Events** (Track Action Success/Failure)
426
+
427
+ These events fire when programmatic actions succeed or fail, giving you complete control over user feedback:
428
+
429
+ #### 🛒 **Cart Action Feedback**
430
+ ```javascript
431
+ // Product addition feedback
432
+ 'lce:actions.cart_product_add_success' // → "Products successfully added to cart"
433
+ // Data: { itemsAdded: number, identifiers: string[] }
434
+
435
+ 'lce:actions.cart_product_add_failed' // → "Products could not be added to cart"
436
+ // Data: { identifiers: string[], error: string }
437
+
438
+ // Promo code feedback
439
+ 'lce:actions.cart_promo_code_applied' // → "Promo code worked - show success!"
440
+ // Data: { applied: true, discountAmount?: number, newTotal?: number }
441
+
442
+ 'lce:actions.cart_promo_code_removed' // → "Promo code removed successfully"
443
+ // Data: { applied: false }
444
+
445
+ 'lce:actions.cart_promo_code_failed' // → "Promo code didn't work - show error"
446
+ // Data: { attempted: true, error: string }
447
+ ```
448
+
449
+ #### 💳 **Checkout Action Feedback**
450
+ ```javascript
451
+ // Product addition feedback
452
+ 'lce:actions.checkout_product_add_success' // → "Products added to checkout successfully"
453
+ // Data: { itemsAdded: number, identifiers: string[] }
454
+
455
+ 'lce:actions.checkout_product_add_failed' // → "Could not add products to checkout"
456
+ // Data: { identifiers: string[], error: string }
457
+
458
+ // Promo code feedback
459
+ 'lce:actions.checkout_promo_code_applied' // → "Checkout discount applied!"
460
+ // Data: { applied: true, discountAmount?: number, newTotal?: number }
461
+
462
+ 'lce:actions.checkout_promo_code_removed' // → "Checkout discount removed"
463
+ // Data: { applied: false }
464
+
465
+ 'lce:actions.checkout_promo_code_failed' // → "Checkout discount failed"
466
+ // Data: { attempted: true, error: string }
467
+
468
+ // Gift card feedback
469
+ 'lce:actions.checkout_gift_card_applied' // → "Gift card applied successfully!"
470
+ // Data: { applied: true, newTotal?: number }
471
+
472
+ 'lce:actions.checkout_gift_card_removed' // → "Gift card removed"
473
+ // Data: { applied: false }
474
+
475
+ 'lce:actions.checkout_gift_card_failed' // → "Gift card could not be applied"
476
+ // Data: { attempted: true, error: string }
477
+ ```
478
+
479
+ #### 🔒 **Security Note**
480
+ Action feedback events **never expose sensitive information** like promo codes or gift card codes. This prevents malicious scripts from stealing sensitive data while still providing rich feedback for legitimate developers.
481
+
482
+ **Example: Secure Action Feedback**
483
+ ```javascript
484
+ // Fire the action (simple)
485
+ await actions.cart.applyPromoCode('SECRET20');
486
+
487
+ // Get feedback (secure)
488
+ window.addEventListener('lce:actions.cart_promo_code_applied', function(event) {
489
+ const { applied, discountAmount, newTotal } = event.detail.data;
490
+ // ✅ You get: success status, discount amount, new total
491
+ // 🚫 You DON'T get: 'SECRET20' (secure!)
492
+
493
+ showSuccessMessage(`🎉 Discount applied! You saved $${discountAmount}!`);
494
+ updateCartTotal(newTotal);
495
+ });
496
+ ```
497
+
498
+ ## Step 3: Real-World Implementation Examples
499
+
500
+ ### 🔔 **NEW Example: Smart Action Feedback with Perfect UX**
501
+ ```javascript
502
+ // Perfect promo code experience with action feedback
503
+ async function applyPromoCodeWithFeedback(code) {
504
+ // Show loading state
505
+ showLoadingMessage('Applying promo code...');
506
+
507
+ // Set up event listeners BEFORE firing action
508
+ const successHandler = (event) => {
509
+ const { applied, discountAmount, newTotal } = event.detail.data;
510
+ hideLoadingMessage();
511
+ showSuccessMessage(`🎉 Success! You saved $${discountAmount}!`);
512
+ updatePriceDisplay(newTotal);
513
+
514
+ // Track successful promo usage
515
+ analytics.track('Promo Applied', {
516
+ discount: discountAmount,
517
+ newTotal: newTotal
518
+ });
519
+
520
+ // Clean up
521
+ cleanup();
522
+ };
523
+
524
+ const failureHandler = (event) => {
525
+ const { attempted, error } = event.detail.data;
526
+ hideLoadingMessage();
527
+ showErrorMessage('Promo code could not be applied. Please try again.');
528
+
529
+ // Track failed attempts (for optimization)
530
+ analytics.track('Promo Failed', { error: error });
531
+
532
+ // Maybe suggest alternatives
533
+ suggestAlternativePromos();
534
+
535
+ // Clean up
536
+ cleanup();
537
+ };
538
+
539
+ const cleanup = () => {
540
+ window.removeEventListener('lce:actions.cart_promo_code_applied', successHandler);
541
+ window.removeEventListener('lce:actions.cart_promo_code_failed', failureHandler);
542
+ };
543
+
544
+ // Attach listeners
545
+ window.addEventListener('lce:actions.cart_promo_code_applied', successHandler);
546
+ window.addEventListener('lce:actions.cart_promo_code_failed', failureHandler);
547
+
548
+ // Fire the action
549
+ await actions.cart.applyPromoCode(code);
550
+ }
551
+
552
+ // Usage: Perfect UX every time!
553
+ applyPromoCodeWithFeedback('SAVE20');
554
+ ```
555
+
556
+ ### 🎯 Example 1: Boost Your Sales with Smart Promotions
557
+ ```javascript
558
+ // Automatically offer discounts for high-value carts
559
+ window.addEventListener('lce:actions.cart_updated', function(event) {
560
+ const cart = event.detail.data.current;
561
+
562
+ if (cart.amounts.total > 200 && !cart.promoCode) {
563
+ // Show them a 10% off popup
564
+ showPopup("Get 10% off orders over $200! Use code: SAVE10");
565
+ }
566
+ });
567
+
568
+ // Track which products make people abandon checkout
569
+ window.addEventListener('lce:actions.checkout_opened', function(event) {
570
+ window.checkoutStartTime = new Date();
571
+ });
572
+
573
+ window.addEventListener('lce:actions.checkout_closed', function(event) {
574
+ // If they close checkout without buying, they abandoned
575
+ if (window.checkoutStartTime) {
576
+ const timeSpent = new Date() - window.checkoutStartTime;
577
+ console.log('Checkout abandoned after', timeSpent/1000, 'seconds');
578
+ // Send this data to your analytics to find problem products
579
+ }
580
+ });
581
+ ```
582
+
583
+ ### 📧 Example 2: Automated Email Marketing
584
+ ```javascript
585
+ // Build an email list from cart abandoners
586
+ window.addEventListener('lce:actions.cart_opened', function(event) {
587
+ // Customer opened cart - start tracking
588
+ window.cartOpenTime = new Date();
589
+ window.cartItems = event.detail.data.items;
590
+ });
591
+
592
+ window.addEventListener('lce:actions.checkout_submit_completed', function(event) {
593
+ // They bought! No need for abandonment email
594
+ window.cartOpenTime = null;
595
+ });
596
+
597
+ // Check every 5 minutes if cart was abandoned
598
+ setInterval(function() {
599
+ if (window.cartOpenTime) {
600
+ const timeSinceOpen = new Date() - window.cartOpenTime;
601
+ if (timeSinceOpen > 15 * 60 * 1000) { // 15 minutes
602
+ // Send abandonment email with cart items
603
+ sendAbandonmentEmail(window.cartItems);
604
+ window.cartOpenTime = null; // Don't send again
605
+ }
606
+ }
607
+ }, 5 * 60 * 1000);
608
+ ```
609
+
610
+ ### 📊 Example 3: Track Your Best-Performing Products
611
+ ```javascript
612
+ // Keep a running tally of product performance
613
+ let productStats = {};
614
+
615
+ window.addEventListener('lce:actions.product_loaded', function(event) {
616
+ const product = event.detail.data;
617
+
618
+ // Initialize stats if first time seeing this product
619
+ if (!productStats[product.id]) {
620
+ productStats[product.id] = {
621
+ name: product.name,
622
+ views: 0,
623
+ addedToCarts: 0,
624
+ conversions: 0
625
+ };
626
+ }
627
+
628
+ productStats[product.id].views++;
629
+ });
630
+
631
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
632
+ const data = event.detail.data;
633
+ // Note: product_add_to_cart only has identifier, not full product details
634
+ if (productStats[data.identifier]) {
635
+ productStats[data.identifier].addedToCarts++;
636
+ }
637
+ });
638
+
639
+ window.addEventListener('lce:actions.checkout_submit_completed', function(event) {
640
+ const order = event.detail.data;
641
+ console.log('Order completed:', order.orderNumber, 'Total:', order.orderTotal);
642
+
643
+ // Send stats to your analytics dashboard
644
+ console.log('Product Performance:', productStats);
645
+
646
+ // Track conversion in analytics
647
+ analytics.track('Purchase', {
648
+ orderNumber: order.orderNumber,
649
+ total: order.orderTotal
650
+ });
651
+ });
652
+ ```
653
+
654
+ ### 🔥 Example 4: Create Urgency with Live Updates
655
+ ```javascript
656
+ // Show live activity to create FOMO (Fear of Missing Out)
657
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
658
+ const data = event.detail.data;
659
+
660
+ // Show notification to other visitors
661
+ showNotification(`Someone just bought product ${data.identifier}! Only 3 left!`);
662
+
663
+ // Update inventory count on the page
664
+ updateInventoryDisplay(data.identifier, -1);
665
+ });
666
+
667
+ window.addEventListener('lce:actions.cart_opened', function(event) {
668
+ // Show how many people are currently viewing this product
669
+ showViewerCount("👀 5 people are looking at this right now");
670
+ });
671
+ ```
672
+
673
+ ## Common Use Cases & How to Implement
674
+
675
+ ### 🛒 **Cart Abandonment Recovery**
676
+ **The Problem**: 70% of carts are abandoned
677
+ **The Solution**: Automatic follow-up based on events
678
+
679
+ ```javascript
680
+ // Set up cart abandonment tracking
681
+ let abandonmentTimer;
682
+
683
+ window.addEventListener('lce:actions.cart_opened', function() {
684
+ // Reset timer each time they open cart
685
+ clearTimeout(abandonmentTimer);
686
+
687
+ abandonmentTimer = setTimeout(function() {
688
+ // After 30 minutes of inactivity, trigger recovery
689
+ triggerCartRecovery();
690
+ }, 30 * 60 * 1000);
691
+ });
692
+
693
+ window.addEventListener('lce:actions.checkout_submit_completed', function() {
694
+ // They completed purchase - cancel recovery
695
+ clearTimeout(abandonmentTimer);
696
+ });
697
+
698
+ function triggerCartRecovery() {
699
+ // Send email, show popup, etc.
700
+ console.log("Time to recover this cart!");
701
+ }
702
+ ```
703
+
704
+ ### 📈 **Revenue Optimization**
705
+ **The Goal**: Increase average order value
706
+ **The Method**: Smart upselling based on behavior
707
+
708
+ ```javascript
709
+ window.addEventListener('lce:actions.product_add_to_cart', function(event) {
710
+ const data = event.detail.data;
711
+
712
+ // Offer upsells based on product identifier
713
+ const upsellMap = {
714
+ 'phone-001': 'phone-cases',
715
+ 'laptop-001': 'laptop-accessories'
716
+ };
717
+
718
+ if (upsellMap[data.identifier]) {
719
+ showUpsell(upsellMap[data.identifier]);
720
+ }
721
+
722
+ // Check if cart is close to free shipping threshold
723
+ const cart = actions.cart.getDetails();
724
+ if (cart.amounts.total > 45 && cart.amounts.total < 50) {
725
+ showMessage("Add $" + (50 - cart.amounts.total) + " more for FREE shipping!");
726
+ }
727
+ });
728
+ ```
729
+
730
+ ### 🎯 **Conversion Tracking**
731
+ **The Need**: Measure what's working
732
+ **The Implementation**: Track the entire funnel
733
+
734
+ ```javascript
735
+ // Track the complete conversion funnel
736
+ const funnelSteps = {
737
+ productView: 0,
738
+ addToCart: 0,
739
+ cartView: 0,
740
+ checkoutStart: 0,
741
+ purchase: 0
742
+ };
743
+
744
+ window.addEventListener('lce:actions.product_loaded', () => funnelSteps.productView++);
745
+ window.addEventListener('lce:actions.product_add_to_cart', () => funnelSteps.addToCart++);
746
+ window.addEventListener('lce:actions.cart_opened', () => funnelSteps.cartView++);
747
+ window.addEventListener('lce:actions.checkout_opened', () => funnelSteps.checkoutStart++);
748
+ window.addEventListener('lce:actions.checkout_submit_completed', () => funnelSteps.purchase++);
749
+
750
+ // Send to your analytics every 10 minutes
751
+ setInterval(() => {
752
+ console.log('Conversion Funnel:', funnelSteps);
753
+ // Send to your dashboard/analytics
754
+ }, 10 * 60 * 1000);
755
+ ```
756
+
757
+ ## Getting Started in 5 Minutes
758
+
759
+ 1. **Pick one goal** from the examples above
760
+ 2. **Copy the code** into your website
761
+ 3. **Replace the console.log** with your actual actions (send email, show popup, etc.)
762
+ 4. **Test it** by going through the flow yourself
763
+ 5. **Monitor the results** and adjust as needed
764
+
765
+ **Need help?** The events automatically work once LiquidCommerce Elements are on your page. Just add the JavaScript code and you're ready to go!