From 60acff2c615fcbcd38a404580be0d0b78ffa60e5 Mon Sep 17 00:00:00 2001 From: masmalsah Date: Tue, 4 Nov 2025 17:03:52 +1300 Subject: [PATCH 1/9] initial push of existing campaign cart docs --- .../api-reference/attribution.md | 440 +++++++ docs/campaign-cart/api-reference/callbacks.md | 341 +++++ .../api-reference/css-classes.md | 292 +++++ .../api-reference/data-attributes.md | 201 +++ docs/campaign-cart/api-reference/events.md | 635 +++++++++ docs/campaign-cart/api-reference/index.md | 32 + .../api-reference/method-summary.md | 178 +++ docs/campaign-cart/api-reference/methods.md | 1130 +++++++++++++++++ docs/campaign-cart/api-reference/profiles.md | 354 ++++++ .../api-reference/url-parameters.md | 342 +++++ docs/campaign-cart/attributes/campaign.md | 69 + docs/campaign-cart/attributes/cart.md | 489 +++++++ docs/campaign-cart/attributes/checkout.md | 278 ++++ docs/campaign-cart/attributes/conditionals.md | 160 +++ docs/campaign-cart/attributes/formatting.md | 147 +++ docs/campaign-cart/attributes/index.md | 34 + docs/campaign-cart/attributes/order.md | 645 ++++++++++ docs/campaign-cart/attributes/overview.md | 74 ++ docs/campaign-cart/attributes/package.md | 190 +++ docs/campaign-cart/attributes/profile.md | 273 ++++ docs/campaign-cart/attributes/selection.md | 230 ++++ .../attributes/url-parameters.md | 314 +++++ docs/campaign-cart/attribution/index.md | 16 + .../attribution/url-parameters.md | 521 ++++++++ docs/campaign-cart/cart-system/buttons.md | 126 ++ docs/campaign-cart/cart-system/index.md | 26 + docs/campaign-cart/cart-system/overview.md | 55 + .../cart-system/quantity-controls.md | 100 ++ docs/campaign-cart/cart-system/selectors.md | 102 ++ .../cart-system/state-management.md | 122 ++ docs/campaign-cart/checkout/index.md | 18 + .../multi-step-implementation-example.md | 249 ++++ .../checkout/spreedly-configuration.md | 309 +++++ .../examples/advanced-customization.md | 625 +++++++++ .../examples/basic-product-page.md | 314 +++++ docs/campaign-cart/examples/checkout-page.md | 590 +++++++++ docs/campaign-cart/examples/index.md | 27 + .../quantity-package-swapper-example.html | 86 ++ .../examples/quantity-package-swapper.js | 224 ++++ .../examples/quantity-package-swapper.md | 348 +++++ .../examples/tier-selector-implementation.md | 450 +++++++ docs/campaign-cart/examples/upsell-flow.md | 532 ++++++++ .../getting-started/configuration.md | 60 + docs/campaign-cart/getting-started/index.md | 25 + .../getting-started/installation.md | 50 + .../getting-started/quick-start.md | 110 ++ .../getting-started/troubleshooting.md | 61 + .../getting-started/url-parameters.md | 167 +++ docs/campaign-cart/guides/accessibility.md | 369 ++++++ docs/campaign-cart/guides/best-practices.md | 319 +++++ docs/campaign-cart/guides/index.md | 26 + docs/campaign-cart/guides/migration.md | 45 + docs/campaign-cart/guides/performance.md | 404 ++++++ docs/campaign-cart/guides/profiles.md | 702 ++++++++++ docs/campaign-cart/index.md | 85 ++ .../multi-currency/flow-chart.md | 189 +++ docs/campaign-cart/multi-currency/index.md | 17 + docs/campaign-cart/multi-currency/overview.md | 324 +++++ docs/campaign-cart/upsells/configuration.md | 92 ++ docs/campaign-cart/upsells/direct-upsells.md | 91 ++ docs/campaign-cart/upsells/index.md | 28 + docs/campaign-cart/upsells/overview.md | 68 + .../campaign-cart/upsells/quantity-upsells.md | 154 +++ .../upsells/selection-upsells.md | 144 +++ docs/campaign-cart/utilities/analytics.md | 319 +++++ docs/campaign-cart/utilities/debugger.md | 376 ++++++ docs/campaign-cart/utilities/exit-intent.md | 473 +++++++ docs/campaign-cart/utilities/fomo.md | 188 +++ docs/campaign-cart/utilities/index.md | 26 + .../campaign-cart/utilities/loading-states.md | 248 ++++ docusaurus.config.js | 11 +- package-lock.json | 195 --- 72 files changed, 16855 insertions(+), 199 deletions(-) create mode 100644 docs/campaign-cart/api-reference/attribution.md create mode 100644 docs/campaign-cart/api-reference/callbacks.md create mode 100644 docs/campaign-cart/api-reference/css-classes.md create mode 100644 docs/campaign-cart/api-reference/data-attributes.md create mode 100644 docs/campaign-cart/api-reference/events.md create mode 100644 docs/campaign-cart/api-reference/index.md create mode 100644 docs/campaign-cart/api-reference/method-summary.md create mode 100644 docs/campaign-cart/api-reference/methods.md create mode 100644 docs/campaign-cart/api-reference/profiles.md create mode 100644 docs/campaign-cart/api-reference/url-parameters.md create mode 100644 docs/campaign-cart/attributes/campaign.md create mode 100644 docs/campaign-cart/attributes/cart.md create mode 100644 docs/campaign-cart/attributes/checkout.md create mode 100644 docs/campaign-cart/attributes/conditionals.md create mode 100644 docs/campaign-cart/attributes/formatting.md create mode 100644 docs/campaign-cart/attributes/index.md create mode 100644 docs/campaign-cart/attributes/order.md create mode 100644 docs/campaign-cart/attributes/overview.md create mode 100644 docs/campaign-cart/attributes/package.md create mode 100644 docs/campaign-cart/attributes/profile.md create mode 100644 docs/campaign-cart/attributes/selection.md create mode 100644 docs/campaign-cart/attributes/url-parameters.md create mode 100644 docs/campaign-cart/attribution/index.md create mode 100644 docs/campaign-cart/attribution/url-parameters.md create mode 100644 docs/campaign-cart/cart-system/buttons.md create mode 100644 docs/campaign-cart/cart-system/index.md create mode 100644 docs/campaign-cart/cart-system/overview.md create mode 100644 docs/campaign-cart/cart-system/quantity-controls.md create mode 100644 docs/campaign-cart/cart-system/selectors.md create mode 100644 docs/campaign-cart/cart-system/state-management.md create mode 100644 docs/campaign-cart/checkout/index.md create mode 100644 docs/campaign-cart/checkout/multi-step-implementation-example.md create mode 100644 docs/campaign-cart/checkout/spreedly-configuration.md create mode 100644 docs/campaign-cart/examples/advanced-customization.md create mode 100644 docs/campaign-cart/examples/basic-product-page.md create mode 100644 docs/campaign-cart/examples/checkout-page.md create mode 100644 docs/campaign-cart/examples/index.md create mode 100644 docs/campaign-cart/examples/quantity-package-swapper-example.html create mode 100644 docs/campaign-cart/examples/quantity-package-swapper.js create mode 100644 docs/campaign-cart/examples/quantity-package-swapper.md create mode 100644 docs/campaign-cart/examples/tier-selector-implementation.md create mode 100644 docs/campaign-cart/examples/upsell-flow.md create mode 100644 docs/campaign-cart/getting-started/configuration.md create mode 100644 docs/campaign-cart/getting-started/index.md create mode 100644 docs/campaign-cart/getting-started/installation.md create mode 100644 docs/campaign-cart/getting-started/quick-start.md create mode 100644 docs/campaign-cart/getting-started/troubleshooting.md create mode 100644 docs/campaign-cart/getting-started/url-parameters.md create mode 100644 docs/campaign-cart/guides/accessibility.md create mode 100644 docs/campaign-cart/guides/best-practices.md create mode 100644 docs/campaign-cart/guides/index.md create mode 100644 docs/campaign-cart/guides/migration.md create mode 100644 docs/campaign-cart/guides/performance.md create mode 100644 docs/campaign-cart/guides/profiles.md create mode 100644 docs/campaign-cart/index.md create mode 100644 docs/campaign-cart/multi-currency/flow-chart.md create mode 100644 docs/campaign-cart/multi-currency/index.md create mode 100644 docs/campaign-cart/multi-currency/overview.md create mode 100644 docs/campaign-cart/upsells/configuration.md create mode 100644 docs/campaign-cart/upsells/direct-upsells.md create mode 100644 docs/campaign-cart/upsells/index.md create mode 100644 docs/campaign-cart/upsells/overview.md create mode 100644 docs/campaign-cart/upsells/quantity-upsells.md create mode 100644 docs/campaign-cart/upsells/selection-upsells.md create mode 100644 docs/campaign-cart/utilities/analytics.md create mode 100644 docs/campaign-cart/utilities/debugger.md create mode 100644 docs/campaign-cart/utilities/exit-intent.md create mode 100644 docs/campaign-cart/utilities/fomo.md create mode 100644 docs/campaign-cart/utilities/index.md create mode 100644 docs/campaign-cart/utilities/loading-states.md diff --git a/docs/campaign-cart/api-reference/attribution.md b/docs/campaign-cart/api-reference/attribution.md new file mode 100644 index 0000000..e7960e8 --- /dev/null +++ b/docs/campaign-cart/api-reference/attribution.md @@ -0,0 +1,440 @@ +# Attribution API + +The SDK provides comprehensive attribution tracking capabilities, including support for custom metadata that can be passed with orders. + +## Simple API via window.next + +The easiest way to manage attribution metadata is through the `window.next` object: + +```javascript +// Add or update a single metadata field +window.next.addMetadata('campaign_id', 'SUMMER2025'); +window.next.addMetadata('influencer_code', 'JANE123'); + +// Set multiple metadata fields at once +window.next.setMetadata({ + campaign_id: 'SUMMER2025', + influencer_code: 'JANE123', + test_variant: 'hero_b', + source_platform: 'instagram' +}); + +// Get current metadata +const metadata = window.next.getMetadata(); +console.log(metadata); + +// Clear all custom metadata (preserves automatic fields) +window.next.clearMetadata(); + +// Set complete attribution data (including standard fields) +window.next.setAttribution({ + utm_source: 'instagram', + utm_campaign: 'summer2025', + affiliate: 'partner123', + metadata: { + custom_field: 'value' + } +}); + +// Get current attribution that will be sent with orders +const attribution = window.next.getAttribution(); +console.log(attribution); + +// Debug attribution data in console +window.next.debugAttribution(); +``` + +## Attribution Object Structure + +When orders are created, the SDK sends attribution data in the following format: + +```typescript +interface Attribution { + affiliate?: string; + funnel?: string; + gclid?: string; + metadata?: Record; // Custom metadata object + subaffiliate1?: string; + subaffiliate2?: string; + subaffiliate3?: string; + subaffiliate4?: string; + subaffiliate5?: string; + utm_campaign?: string; + utm_content?: string; + utm_medium?: string; + utm_source?: string; + utm_term?: string; + everflow_transaction_id?: string; +} +``` + +## Accessing Attribution Store + +The attribution store is available globally after SDK initialization: + +```javascript +// Access via window object +window.NextAttributionStore + +// Or via NextStores +const { useAttributionStore } = window.NextStores; +const attributionStore = useAttributionStore.getState(); +``` + +## Methods + +### updateAttribution(data) + +Updates attribution data including custom metadata. + +```javascript +window.NextAttributionStore.updateAttribution({ + affiliate: 'partner_123', + utm_source: 'instagram', + metadata: { + custom_field: 'value', + tracking_id: 'abc123' + } +}); +``` + +**Parameters:** +- `data` (Partial<AttributionState>): Object containing attribution fields to update + +### getAttributionForApi() + +Returns the current attribution data formatted for API submission. + +```javascript +const attribution = window.NextAttributionStore.getAttributionForApi(); +console.log(attribution); +// Output: { affiliate: '...', metadata: {...}, utm_source: '...', ... } +``` + +**Returns:** Attribution object ready for API + +### setFunnelName(funnel) + +Sets the funnel name for attribution tracking. Once set, it persists and cannot be overwritten. + +```javascript +window.NextAttributionStore.setFunnelName('summer_sale_2025'); +``` + +**Parameters:** +- `funnel` (string): The funnel identifier + +### setEverflowClickId(evclid) + +Sets the Everflow click ID for tracking. + +```javascript +window.NextAttributionStore.setEverflowClickId('ef_click_12345'); +``` + +**Parameters:** +- `evclid` (string): Everflow click identifier + +### debug() + +Outputs detailed attribution information to the console for debugging. + +```javascript +window.NextAttributionStore.debug(); +// Logs comprehensive attribution data to console +``` + +### clearPersistedFunnel() + +Clears the persisted funnel name from storage. + +```javascript +window.NextAttributionStore.clearPersistedFunnel(); +``` + +## Custom Metadata + +The `metadata` field accepts any custom properties you need to track with orders. + +### Automatic Metadata Collection + +The SDK automatically collects these metadata fields: + +```javascript +{ + landing_page: string; // Entry page URL + referrer: string; // Referring URL + device: string; // Device info + device_type: 'mobile' | 'desktop'; + domain: string; // Current domain + timestamp: number; // Visit timestamp + conversion_timestamp?: number; + + // Facebook tracking (when available) + fb_fbp?: string; + fb_fbc?: string; + fb_pixel_id?: string; + fbclid?: string; + + // Everflow tracking (when available) + everflow_transaction_id?: string; + sg_evclid?: string; +} +``` + +### Adding Custom Metadata + +Add any custom tracking data your business requires: + +```javascript +window.NextAttributionStore.updateAttribution({ + metadata: { + // Marketing campaign data + campaign_id: 'SUMMER2025', + campaign_version: 'v2', + creative_id: 'hero_banner_b', + + // Partner/Influencer tracking + influencer_code: 'JANE123', + partner_id: 'partner_456', + commission_tier: 'gold', + + // Platform-specific IDs + tiktok_click_id: 'ttc_abc123', + pinterest_id: 'pin_xyz789', + snapchat_id: 'snap_456', + reddit_campaign: 'sponsored_post_789', + + // A/B testing + test_group: 'variant_b', + test_id: 'homepage_hero_test', + + // Geographic/Store data + store_location: 'NYC_flagship', + region: 'northeast', + + // Custom business logic + customer_segment: 'vip', + loyalty_tier: 'platinum', + referral_program_id: 'friend_2025', + + // Nested objects are supported + advanced_tracking: { + session_id: 'sess_xyz', + interaction_count: 5, + time_on_site: 300 + } + } +}); +``` + +## Examples + +### Example 1: E-commerce Platform Integration + +```javascript +// Track marketplace attribution +function trackMarketplaceSource(marketplace, sellerId, listingId) { + window.NextAttributionStore.updateAttribution({ + utm_source: marketplace, + utm_medium: 'marketplace', + affiliate: sellerId, + metadata: { + marketplace_name: marketplace, + seller_id: sellerId, + listing_id: listingId, + marketplace_category: 'electronics', + marketplace_rank: 5, + fulfilled_by: 'merchant', + prime_eligible: true, + promotion_type: 'lightning_deal' + } + }); +} + +// Usage +trackMarketplaceSource('amazon', 'SELLER123', 'B08XYZ123'); +``` + +### Example 2: Multi-Touch Attribution + +```javascript +// Track multiple touchpoints in customer journey +function trackTouchpoint(touchpointData) { + const currentMetadata = window.NextAttributionStore.getAttributionForApi().metadata || {}; + + // Append to touchpoint history + const touchpoints = currentMetadata.touchpoints || []; + touchpoints.push({ + timestamp: Date.now(), + channel: touchpointData.channel, + action: touchpointData.action, + value: touchpointData.value + }); + + window.NextAttributionStore.updateAttribution({ + metadata: { + ...currentMetadata, + touchpoints: touchpoints, + last_touchpoint: touchpointData.channel, + touchpoint_count: touchpoints.length, + attribution_model: 'linear' // or 'first_touch', 'last_touch', etc. + } + }); +} + +// Track various touchpoints +trackTouchpoint({ channel: 'email', action: 'opened', value: 'welcome_series' }); +trackTouchpoint({ channel: 'social', action: 'clicked', value: 'instagram_story' }); +trackTouchpoint({ channel: 'search', action: 'clicked', value: 'brand_term' }); +``` + +### Example 3: Retail & Offline Attribution + +```javascript +// Bridge offline and online attribution +function trackOfflineAttribution(storeData) { + window.NextAttributionStore.updateAttribution({ + utm_source: 'retail', + utm_medium: 'in_store', + utm_campaign: storeData.campaign || 'store_visit', + metadata: { + // Store information + store_id: storeData.storeId, + store_name: storeData.storeName, + store_region: storeData.region, + store_type: storeData.type, // 'flagship', 'outlet', 'pop_up' + + // Staff attribution + associate_id: storeData.associateId, + associate_name: storeData.associateName, + department: storeData.department, + + // In-store journey + kiosk_used: storeData.kioskUsed, + qr_scanned: storeData.qrScanned, + fitting_room_tech: storeData.fittingRoomTech, + + // Transaction bridging + in_store_cart_id: storeData.cartId, + pos_system: 'square', + store_promo_code: storeData.promoCode, + + // Customer experience + appointment_booked: storeData.appointmentBooked, + personal_shopper: storeData.personalShopper, + curbside_pickup: false, + + // Timing + store_visit_timestamp: storeData.visitTime, + offline_to_online_hours: storeData.hoursSinceVisit + } + }); +} +``` + +### Example 4: Content Attribution + +```javascript +// Track content-driven conversions +function trackContentAttribution(contentData) { + window.NextAttributionStore.updateAttribution({ + utm_source: contentData.platform, + utm_medium: 'content', + utm_campaign: contentData.campaign, + utm_content: contentData.contentId, + metadata: { + // Content details + content_type: contentData.type, // 'blog', 'video', 'podcast', 'webinar' + content_id: contentData.contentId, + content_title: contentData.title, + content_author: contentData.author, + content_category: contentData.category, + publish_date: contentData.publishDate, + + // Engagement metrics + read_time_seconds: contentData.readTime, + scroll_depth: contentData.scrollDepth, + video_watch_percentage: contentData.watchPercentage, + + // Content journey + content_path: contentData.contentPath, // ['blog_1', 'video_2', 'product_page'] + content_touches: contentData.touches, + + // SEO/Discovery + entry_keyword: contentData.keyword, + discovery_method: contentData.discovery, // 'search', 'recommendation', 'social' + + // Monetization + paywall_shown: contentData.paywallShown, + subscription_prompt: contentData.subscriptionPrompt, + native_ad_clicked: contentData.nativeAdClicked + } + }); +} +``` + +## URL Parameter Auto-Collection + +The SDK automatically collects attribution data from URL parameters on page load: + +- UTM parameters: `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term` +- Google Ads: `gclid` +- Facebook: `fbclid` +- Affiliate: `affiliate`, `subaffiliate1-5` +- Funnel: `funnel` +- Custom: Any parameter prefixed with `next_` is added to metadata + +Example URL: +``` +https://example.com/products? + utm_source=instagram& + utm_campaign=summer2025& + affiliate=partner123& + next_custom_id=xyz789& + next_segment=vip +``` + +## Session Persistence + +Attribution data persists throughout the user's session: + +- **Session Storage**: Primary attribution data +- **Local Storage**: Funnel names and Everflow IDs +- **Duration**: Cleared when browser session ends + +## Best Practices + +1. **Initialize Early**: Set attribution data as soon as possible in the user journey +2. **Avoid PII**: Never include personally identifiable information (emails, names, etc.) +3. **Use Consistent Keys**: Establish naming conventions for metadata fields +4. **Validate Data**: Ensure metadata values are valid JSON types +5. **Document Fields**: Maintain documentation of custom metadata fields +6. **Test Integration**: Verify attribution data appears correctly in orders + +## Debugging + +```javascript +// Check current attribution state +window.NextAttributionStore.debug(); + +// Monitor attribution changes +const store = window.NextStores.useAttributionStore.getState(); +const unsubscribe = window.NextStores.useAttributionStore.subscribe( + (state) => console.log('Attribution updated:', state) +); + +// View attribution that will be sent with order +console.log('API Attribution:', window.NextAttributionStore.getAttributionForApi()); +``` + +## Common Issues + +### Issue: Metadata not appearing in orders +**Solution**: Ensure you're calling `updateAttribution()` before order creation and that the metadata object contains valid JSON values. + +### Issue: Funnel name not updating +**Solution**: Funnel names are write-once. Use `clearPersistedFunnel()` to reset if needed during development. + +### Issue: Attribution data lost on page refresh +**Solution**: Attribution persists in session storage. If you need longer persistence, implement your own storage solution and restore on page load. \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/callbacks.md b/docs/campaign-cart/api-reference/callbacks.md new file mode 100644 index 0000000..ed99d12 --- /dev/null +++ b/docs/campaign-cart/api-reference/callbacks.md @@ -0,0 +1,341 @@ +# Callbacks + +The Next Commerce SDK provides two callback systems for different purposes: + +1. **Initialization Callbacks** (`window.nextReady`) - For code that needs to run after SDK loads +2. **Lifecycle Callbacks** (`next.registerCallback`) - For hooking into SDK operations + +## Initialization Callbacks (window.nextReady) + +The `window.nextReady` queue ensures your code runs after the SDK is fully initialized. + +### Basic Usage + +```javascript +// Queue a callback before SDK loads +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + // SDK is ready - window.next is available + console.log('Cart count:', next.getCartCount()); + next.addItem({ packageId: 123 }); +}); +``` + +### Callback Patterns + +The SDK supports two callback patterns: + +#### Pattern 1: Without SDK Parameter (Recommended) + +```javascript +window.nextReady.push(function() { + // Access SDK via global window.next + next.addItem({ packageId: 123 }); + next.trackViewItemList(['1', '2', '3']); +}); +``` + +#### Pattern 2: With SDK Parameter + +```javascript +window.nextReady.push(function(sdk) { + // SDK passed as parameter + sdk.addItem({ packageId: 123 }); + sdk.trackViewItemList(['1', '2', '3']); +}); +``` + +### Multiple Callbacks + +You can queue multiple callbacks: + +```javascript +// First callback - initialize tracking +window.nextReady.push(function() { + next.trackViewItemList(['1', '2', '3']); +}); + +// Second callback - set up event listeners +window.nextReady.push(function() { + next.on('cart:updated', function(data) { + console.log('Cart updated:', data); + }); +}); + +// Third callback - check for saved cart +window.nextReady.push(function() { + if (next.getCartCount() === 0) { + console.log('Cart is empty'); + } +}); +``` + +### After SDK Loads + +Once the SDK is loaded, `window.nextReady` becomes a direct executor: + +```javascript +// After SDK initialization, callbacks execute immediately +window.nextReady.push(function() { + console.log('This runs immediately after SDK is ready'); +}); +``` + +### Error Handling + +Callbacks are wrapped in try-catch to prevent one callback from breaking others: + +```javascript +window.nextReady.push(function() { + try { + // Your code here + customIntegration(); + } catch (error) { + console.error('Custom integration failed:', error); + } +}); +``` + +## Lifecycle Callbacks (Legacy) + +The SDK also supports lifecycle callbacks for specific operations. This is a legacy feature primarily for backwards compatibility. + +### Available Callback Types + +```javascript +// CallbackType values: +'beforeRender' // Before cart UI renders +'afterRender' // After cart UI renders +'beforeCheckout' // Before checkout starts +'afterCheckout' // After checkout completes +'beforeRedirect' // Before order redirect +'itemAdded' // After item added to cart +'itemRemoved' // After item removed from cart +'cartCleared' // After cart cleared +``` + +### Registering Callbacks + +```javascript +window.nextReady.push(function() { + // Register a callback + next.registerCallback('itemAdded', function(data) { + console.log('Item added to cart:', data); + // data contains: cartLines, cartTotals, campaignData, appliedCoupons + }); + + // Register multiple callbacks + next.registerCallback('beforeCheckout', function(data) { + console.log('Checkout starting with:', data.cartTotals); + }); +}); +``` + +### Callback Data Structure + +All lifecycle callbacks receive a `CallbackData` object: + +```typescript +{ + cartLines: EnrichedCartLine[]; // Cart items with full details + cartTotals: CartTotals; // Pricing information + campaignData: Campaign | null; // Campaign configuration + appliedCoupons: AppliedCoupon[]; // Active discounts +} +``` + +### Unregistering Callbacks + +```javascript +// Store reference to callback function +const myCallback = function(data) { + console.log('Cart data:', data); +}; + +// Register callback +next.registerCallback('cartCleared', myCallback); + +// Later, unregister it +next.unregisterCallback('cartCleared', myCallback); +``` + +### Example: Analytics Integration + +```javascript +window.nextReady.push(function() { + // Track when items are added + next.registerCallback('itemAdded', function(data) { + if (window.gtag) { + const lastItem = data.cartLines[data.cartLines.length - 1]; + gtag('event', 'add_to_cart', { + currency: 'USD', + value: lastItem.price, + items: [{ + item_id: lastItem.packageId, + item_name: lastItem.name, + price: lastItem.price, + quantity: lastItem.quantity + }] + }); + } + }); + + // Track checkout start + next.registerCallback('beforeCheckout', function(data) { + if (window.gtag) { + gtag('event', 'begin_checkout', { + currency: 'USD', + value: data.cartTotals.total.value, + items: data.cartLines.map(item => ({ + item_id: item.packageId, + item_name: item.name, + price: item.price, + quantity: item.quantity + })) + }); + } + }); +}); +``` + +## Best Practices + +### 1. Always Initialize the Queue + +```javascript +// Ensure queue exists before pushing +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + // Your code +}); +``` + +### 2. Check SDK Availability + +```javascript +if (window.next) { + // SDK already loaded, use directly + next.addItem({ packageId: 123 }); +} else { + // Queue for later + window.nextReady = window.nextReady || []; + window.nextReady.push(function() { + next.addItem({ packageId: 123 }); + }); +} +``` + +### 3. Prefer Events Over Callbacks + +For most use cases, SDK events are more flexible than lifecycle callbacks: + +```javascript +// Preferred: Use events +next.on('cart:item-added', function(data) { + console.log('Item added:', data); +}); + +// Legacy: Lifecycle callbacks +next.registerCallback('itemAdded', function(data) { + console.log('Item added:', data); +}); +``` + +### 4. Clean Up When Done + +```javascript +// Store references for cleanup +const callbacks = { + itemAdded: function(data) { /* ... */ }, + cartCleared: function(data) { /* ... */ } +}; + +// Register callbacks +Object.entries(callbacks).forEach(([type, callback]) => { + next.registerCallback(type, callback); +}); + +// Later, clean up +Object.entries(callbacks).forEach(([type, callback]) => { + next.unregisterCallback(type, callback); +}); +``` + +## Common Patterns + +### Initialization with Feature Detection + +```javascript +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + // Check for features + if (next.fomo) { + next.fomo({ displayDuration: 5000 }); + } + + if (next.exitIntent) { + next.exitIntent({ + image: '/offers/exit-discount.jpg', + action: function() { + next.applyCoupon('EXIT20'); + } + }); + } +}); +``` + +### Conditional Loading + +```javascript +// Only load on specific pages +if (document.body.classList.contains('product-page')) { + window.nextReady = window.nextReady || []; + window.nextReady.push(function() { + // Product page specific code + const productId = document.querySelector('[data-product-id]').dataset.productId; + next.trackViewItem(productId); + }); +} +``` + +### Error Recovery + +```javascript +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + try { + // Primary integration + setupCustomCheckout(); + } catch (error) { + console.error('Custom checkout failed:', error); + + // Fallback behavior + next.on('checkout:started', function() { + console.log('Using default checkout'); + }); + } +}); +``` + +## Migration from Legacy Patterns + +If migrating from older SDK versions: + +```javascript +// Old pattern (direct access) +if (window.MySDK) { + MySDK.addToCart(123); +} + +// New pattern (with ready queue) +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + next.addItem({ packageId: 123 }); +}); +``` + +## Related Documentation + +- [Events](./events.md) - For reactive event handling +- [Methods](./methods.md) - For available SDK methods +- [Getting Started](../getting-started/quick-start.md) - For basic setup \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/css-classes.md b/docs/campaign-cart/api-reference/css-classes.md new file mode 100644 index 0000000..2b81c90 --- /dev/null +++ b/docs/campaign-cart/api-reference/css-classes.md @@ -0,0 +1,292 @@ +# CSS Classes + +The Next Commerce JS SDK automatically applies CSS classes to elements based on their state. + +## State Classes + +### Cart Item States + +#### .next-in-cart +Applied to elements when their package is in the cart: + +```html + + + + +
+ +
+``` + +#### .next-active +Applied to toggle buttons when active: + +```html + +``` + +### Selection States + +#### .next-selected +Applied to selected selector cards: + +```html +
+ Selected Option +
+``` + +#### .next-selector-active +Applied to selector container when it has a selection: + +```html +
+ +
+``` + +### Button States + +#### .next-disabled +Applied to disabled action buttons: + +```html + + +``` + +## Page States + +### .next-display-ready +Applied to `` when SDK is initialized: + +```html + + + +``` + +Usage: +```css +/* Hide content until ready */ +html:not(.next-display-ready) [data-next-display] { + visibility: hidden; +} +``` + +### .next-loading +Applied during data loading operations: + +```html + + + +``` + +## Loading State Classes + +Elements with `data-next-await` get special treatment: + +```css +/* Before SDK ready */ +html:not(.next-display-ready) [data-next-await] { + /* Shows loading skeleton */ +} + +/* After SDK ready */ +html.next-display-ready [data-next-await] { + /* Normal display */ +} +``` + +## Styling Examples + +### Toggle Button States + +```css +/* Default state */ +button[data-next-toggle] { + background: #007bff; + color: white; +} + +/* When item is in cart */ +button[data-next-toggle].next-in-cart { + background: #28a745; +} + +/* Active state */ +button[data-next-toggle].next-active { + background: #dc3545; +} + +/* Disabled state */ +button.next-disabled { + opacity: 0.5; + cursor: not-allowed; +} +``` + +### Product Card States + +```css +/* Product card container */ +.product-card { + border: 1px solid #ddd; + transition: all 0.3s ease; +} + +/* When product is in cart */ +.product-card.next-in-cart { + border-color: #28a745; + background: #f8fff9; +} + +/* Style children based on parent state */ +.product-card.next-in-cart .add-button { + display: none; +} + +.product-card.next-in-cart .remove-button { + display: block; +} +``` + +### Selector Card States + +```css +/* Selector cards */ +[data-next-selector-card] { + border: 2px solid #ddd; + cursor: pointer; + transition: all 0.2s ease; +} + +/* Hover state */ +[data-next-selector-card]:hover { + border-color: #007bff; +} + +/* Selected state */ +[data-next-selector-card].next-selected { + border-color: #007bff; + background: #e7f3ff; +} + +/* Selected indicator */ +[data-next-selector-card].next-selected::after { + content: '✓'; + position: absolute; + top: 10px; + right: 10px; + color: #007bff; +} +``` + +### Loading States + +```css +/* Loading skeleton animation */ +[data-next-await]::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: loading 1.5s infinite; +} + +@keyframes loading { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +/* Hide skeleton when ready */ +html.next-display-ready [data-next-await]::before { + display: none; +} +``` + +## Advanced Patterns + +### Multi-State Styling + +```css +/* Combine multiple states */ +.product-card.next-in-cart [data-next-toggle].next-active { + background: #dc3545; + color: white; +} + +/* Different styles for different pages */ +html[data-page-type="checkout"] .next-in-cart { + /* Checkout-specific styles */ +} +``` + +### Animation on State Change + +```css +/* Animate when item added to cart */ +@keyframes addedToCart { + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +.product-card:not(.next-in-cart) { + animation: none; +} + +.product-card.next-in-cart { + animation: addedToCart 0.3s ease; +} +``` + +### Responsive State Styling + +```css +/* Mobile adjustments */ +@media (max-width: 768px) { + [data-next-selector-card].next-selected { + border-width: 3px; + } + + button[data-next-toggle].next-active { + font-size: 14px; + padding: 10px; + } +} +``` + +## Best Practices + +1. **Use CSS Classes**: Style based on SDK classes, not attributes +2. **Smooth Transitions**: Add transitions for state changes +3. **Clear Visual Feedback**: Make states obvious to users +4. **Accessibility**: Ensure state changes are perceivable +5. **Performance**: Use CSS transforms over layout changes + +## Class Reference Table + +| Class | Applied To | When | +|-------|------------|------| +| `.next-in-cart` | Elements with package ID | Package is in cart | +| `.next-active` | Toggle buttons | Toggle is active | +| `.next-selected` | Selector cards | Card is selected | +| `.next-selector-active` | Selector container | Has selection | +| `.next-disabled` | Action buttons | Action unavailable | +| `.next-display-ready` | `` | SDK initialized | +| `.next-loading` | `` | Loading data | \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/data-attributes.md b/docs/campaign-cart/api-reference/data-attributes.md new file mode 100644 index 0000000..7f97b7f --- /dev/null +++ b/docs/campaign-cart/api-reference/data-attributes.md @@ -0,0 +1,201 @@ +# Complete Data Attributes Reference + +Comprehensive reference of all Next Commerce JS SDK HTML data attributes. + +## Display Attributes + +### Basic Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display` | Display dynamic data | `data-next-display="cart.total"` | +| `data-next-await` | Show loading skeleton | `data-next-await` | + +### Campaign Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display="campaign.name"` | Campaign name | My Campaign | +| `data-next-display="campaign.currency"` | Currency code | USD | +| `data-next-display="campaign.language"` | Language code | en | + +### Package Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display="package.name"` | Package name | Premium Bundle | +| `data-next-display="package.price"` | Formatted price | $99.99 | +| `data-next-display="package.price_total"` | Total price | $299.97 | +| `data-next-display="package.qty"` | Quantity in package | 3 | +| `data-next-display="package.savingsPercentage"` | Savings % | 30% | +| `data-next-display="package.image"` | Image URL | https://... | + +### Cart Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display="cart.total"` | Cart total | $199.99 | +| `data-next-display="cart.quantity"` | Total items | 5 | +| `data-next-display="cart.subtotal"` | Subtotal | $179.99 | +| `data-next-display="cart.shipping"` | Shipping cost | $20.00 | +| `data-next-display="cart.discounts"` | Discount amount | -$10.00 | + +### Selection Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display="selection.{id}.name"` | Selected name | Package A | +| `data-next-display="selection.{id}.price"` | Selected price | $99.99 | +| `data-next-display="selection.{id}.total"` | Selected total | $299.97 | + +### Order Display +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-display="order.number"` | Order number | #12345 | +| `data-next-display="order.total"` | Order total | $199.99 | +| `data-next-display="order.customer.name"` | Customer name | John Doe | +| `data-next-display="order.customer.email"` | Email | john@example.com | + +## Conditional Attributes + +### Show/Hide +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-show` | Show when true | `data-next-show="cart.hasItems"` | +| `data-next-hide` | Hide when true | `data-next-hide="cart.isEmpty"` | + +### Common Conditionals +| Attribute | Description | +|-----------|-------------| +| `data-next-show="cart.isEmpty"` | Show when cart empty | +| `data-next-show="cart.hasItems"` | Show when cart has items | +| `data-next-show="cart.total > 50"` | Show when total over $50 | +| `data-next-show="package.hasSavings"` | Show when package has savings | +| `data-next-show="order.exists"` | Show when order found | + +### Profile Conditionals +| Attribute | Description | Example | +|-----------|-------------|---------| +| `data-next-show-if-profile` | Show when profile active | `data-next-show-if-profile="black_friday"` | +| `data-next-hide-if-profile` | Hide when profile active | `data-next-hide-if-profile="regular"` | +| `data-next-show="profile.active === 'id'"` | Show when specific profile | `data-next-show="profile.active === 'vip'"` | +| `data-next-show="profile.is('id')"` | Profile check function | `data-next-show="profile.is('sale')"` | + +## Action Attributes + +### Cart Actions +| Attribute | Description | Required With | +|-----------|-------------|---------------| +| `data-next-action="add-to-cart"` | Add to cart action | `data-next-package-id` | +| `data-next-package-id` | Package to add | Number | +| `data-next-quantity` | Quantity to add | Number (optional) | +| `data-next-url` | Redirect after add | URL (optional) | +| `data-next-clear-cart` | Clear cart first | true/false (optional) | + +### Toggle Actions +| Attribute | Description | Required With | +|-----------|-------------|---------------| +| `data-next-toggle` | Toggle item in cart | `data-next-package-id` | +| `data-add-text` | Text when not in cart | String (optional) | +| `data-remove-text` | Text when in cart | String (optional) | +| `data-next-package-sync` | Sync with packages | Package IDs (optional) | + +## Selector Attributes + +### Container +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-next-cart-selector` | Selector container | - | +| `data-next-selector-id` | Unique selector ID | String | +| `data-next-selection-mode` | Selection mode | `swap`, `select` | + +### Cards +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-next-selector-card` | Selector option | - | +| `data-next-package-id` | Package for card | Number | +| `data-next-selected` | Default selection | `true` | + +## Upsell Attributes + +### Direct Upsell +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-next-upsell="offer"` | Upsell container | - | +| `data-next-package-id` | Package to offer | Number | +| `data-next-upsell-action="add"` | Accept upsell | - | +| `data-next-upsell-action="skip"` | Decline upsell | - | + +### Selection Upsell +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-next-upsell-selector` | Selection container | - | +| `data-next-upsell-option` | Upsell option | - | +| `data-next-upsell-select` | Dropdown select | String ID | + +### Quantity Upsell +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-next-upsell-quantity="increase"` | Increase qty | - | +| `data-next-upsell-quantity="decrease"` | Decrease qty | - | +| `data-next-upsell-quantity="display"` | Show qty | - | +| `data-next-upsell-quantity-toggle` | Set specific qty | Number | + +## Formatting Attributes + +### Format Types +| Attribute | Description | Values | +|-----------|-------------|---------| +| `data-format` | Value format | `currency`, `number` | +| `data-hide-if-zero` | Hide if zero | `true` | +| `data-hide-if-false` | Hide if false | `true` | +| `data-hide-zero-cents` | Hide cents if .00 | `true` | +| `data-multiply-by` | Multiply value | Number | +| `data-divide-by` | Divide value | Number | + +## Profile Attributes + +### Profile Switcher +| Attribute | Description | Values | +|-----------|-------------|--------| +| `data-next-profile` | Profile switcher button | Profile ID | +| `data-next-profile-selector` | Profile dropdown selector | - | +| `data-next-clear-cart` | Clear cart on switch | `true`/`false` | +| `data-next-preserve-quantities` | Keep item quantities | `true`/`false` | +| `data-next-active-text` | Text when profile active | String | +| `data-next-inactive-text` | Text when profile inactive | String | + +## State Classes (Automatic) + +### Cart State +| Class | Applied When | +|-------|--------------| +| `.next-in-cart` | Item is in cart | +| `.next-active` | Toggle is active | +| `.next-disabled` | Action disabled | + +### Selection State +| Class | Applied When | +|-------|--------------| +| `.next-selected` | Option selected | +| `.next-selector-active` | Has selection | + +### Page State +| Class | Applied When | +|-------|--------------| +| `.next-display-ready` | SDK initialized | +| `.next-loading` | Loading data | + +### Profile State +| Class | Applied When | +|-------|--------------| +| `.next-profile-active` | Profile is active | +| `.next-profile-switcher` | Profile switcher element | +| `.next-profile-selector` | Profile selector element | + +## Meta Tag Configuration + +| Meta Name | Description | Values | +|-----------|-------------|---------| +| `next-api-key` | Campaign API key | String | +| `next-debug` | Debug mode | `true`/`false` | +| `next-page-type` | Page type | `product`, `checkout`, `upsell`, `receipt` | +| `next-next-url` | Post-order URL | URL path | +| `next-prevent-back-navigation` | Prevent back | `true`/`false` | +| `next-upsell-accept-url` | Upsell accept URL | URL path | +| `next-upsell-decline-url` | Upsell decline URL | URL path | \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/events.md b/docs/campaign-cart/api-reference/events.md new file mode 100644 index 0000000..b3a931b --- /dev/null +++ b/docs/campaign-cart/api-reference/events.md @@ -0,0 +1,635 @@ +# Events + +The Next Commerce JS SDK emits events that you can listen to for tracking and custom functionality. The SDK has 34 internal events that can be listened to via `next.on()`. + +## Event Categories + +- **Cart Events** - Track cart state changes +- **Checkout Events** - Monitor checkout process +- **Payment Events** - Handle payment states +- **Order Events** - Track order completion +- **Campaign Events** - Campaign data loading +- **Coupon Events** - Discount code usage +- **Upsell Events** - Post-purchase upsell tracking +- **User Events** - User authentication +- **Behavioral Events** - FOMO, exit intent +- **Profile Events** - Profile management and switching +- **System Events** - SDK state, errors, routing + +## SDK Initialization + +### next:initialized + +Fired when SDK is fully loaded and ready (DOM event): + +```javascript +// Listen on document for initialization +document.addEventListener('next:initialized', function(event) { + console.log('SDK is ready at:', event.detail.timestamp); + console.log('Version:', event.detail.version); + // Safe to use SDK methods here +}); +``` + +## Cart Events + +### cart:updated + +Fired whenever cart contents change (most common event): + +```javascript +next.on('cart:updated', (cartState) => { + console.log('Cart updated:', cartState); + // cartState includes: items, totals, enrichedItems, etc. + console.log('Total items:', cartState.totalQuantity); + console.log('Cart total:', cartState.totals.total.formatted); +}); +``` + +### cart:item-added + +Fired when item is added to cart: + +```javascript +next.on('cart:item-added', (data) => { + console.log('Item added:', data); + // data includes: packageId, quantity, item details +}); +``` + +### cart:item-removed + +Fired when item is removed from cart: + +```javascript +next.on('cart:item-removed', (data) => { + console.log('Item removed:', data); + // data includes: packageId, item details that were removed +}); +``` + +### cart:quantity-changed + +Fired when item quantity is updated: + +```javascript +next.on('cart:quantity-changed', (data) => { + console.log('Quantity changed:', data); + // data includes: packageId, oldQuantity, newQuantity +}); +``` + +### cart:package-swapped + +Fired when a package is swapped for another: + +```javascript +next.on('cart:package-swapped', (data) => { + console.log('Package swapped:', data); + // data includes: previousPackageId, newPackageId, previousItem, newItem, priceDifference, source +}); +``` + +## Checkout & Order Events + +### checkout:started + +Fired when checkout process begins: + +```javascript +next.on('checkout:started', (data) => { + console.log('Checkout started:', data); + // data includes: cart items, totals +}); +``` + +### checkout:form-initialized + +Fired when checkout form is ready: + +```javascript +next.on('checkout:form-initialized', () => { + console.log('Checkout form ready'); + // Form is now initialized and ready for input +}); +``` + +### order:completed + +Fired when order is successfully completed: + +```javascript +next.on('order:completed', (order) => { + console.log('Order completed:', order); + // order includes: ref_id, total, lines, customer info +}); +``` + +## Payment Events + +### payment:tokenized + +Fired when payment is successfully tokenized: + +```javascript +next.on('payment:tokenized', (data) => { + console.log('Payment tokenized:', data); + // data includes: token info, payment method +}); +``` + +### payment:error + +Fired when payment processing fails: + +```javascript +next.on('payment:error', (error) => { + console.error('Payment failed:', error); + // error includes: message, code, details +}); +``` + +### express-checkout:started + +Fired when express checkout (PayPal, Apple Pay, etc.) begins: + +```javascript +next.on('express-checkout:started', (data) => { + console.log('Express checkout started:', data); + // data includes: method type (paypal, apple, google) +}); +``` + +## Campaign & Configuration Events + +### campaign:loaded + +Fired when campaign data is loaded: + +```javascript +next.on('campaign:loaded', (campaign) => { + console.log('Campaign loaded:', campaign); + // campaign includes: packages, settings, currency, etc. +}); +``` + +### config:updated + +Fired when SDK configuration changes: + +```javascript +next.on('config:updated', (config) => { + console.log('Config updated:', config); + // config includes: all SDK settings +}); +``` + +## Coupon Events + +### coupon:applied + +Fired when coupon is successfully applied: + +```javascript +next.on('coupon:applied', (coupon) => { + console.log('Coupon applied:', coupon); + // coupon includes: code, discount amount, type +}); +``` + +### coupon:removed + +Fired when coupon is removed: + +```javascript +next.on('coupon:removed', (code) => { + console.log('Coupon removed:', code); + // code is the removed coupon code string +}); +``` + +## Upsell Events + +### upsell:viewed + +Fired when upsell offer is displayed: + +```javascript +next.on('upsell:viewed', (data) => { + console.log('Upsell viewed:', data); + // data includes: packageId, offer details +}); +``` + +### upsell:accepted + +Fired when user accepts an upsell: + +```javascript +next.on('upsell:accepted', (data) => { + console.log('Upsell accepted:', data); + // data includes: packageId, quantity, value +}); +``` + +### upsell:added + +Fired when upsell is successfully added to order: + +```javascript +next.on('upsell:added', (data) => { + console.log('Upsell added:', data); + // data includes: packageId, order, value +}); +``` + +### upsell:skipped + +Fired when user skips/declines an upsell: + +```javascript +next.on('upsell:skipped', (data) => { + console.log('Upsell skipped:', data); + // data includes: packageId, reason +}); +``` + +## User Events + +### user:logged-in + +Fired when user logs in: + +```javascript +next.on('user:logged-in', (data) => { + console.log('User logged in:', data); + // data includes: user info, email +}); +``` + +### user:logged-out + +Fired when user logs out: + +```javascript +next.on('user:logged-out', (data) => { + console.log('User logged out:', data); +}); +``` + +## Behavioral Events + +### fomo:shown + +Fired when FOMO notification appears: + +```javascript +next.on('fomo:shown', (data) => { + console.log('FOMO shown:', data); + // data includes: customer name, product, location +}); +``` + +### exit-intent:clicked + +Fired when user clicks on exit intent popup: + +```javascript +next.on('exit-intent:clicked', (data) => { + console.log('Exit intent clicked:', data); + // data includes: imageUrl, template +}); +``` + +### exit-intent:dismissed + +Fired when exit intent popup is dismissed: + +```javascript +next.on('exit-intent:dismissed', (data) => { + console.log('Exit intent dismissed:', data); + // data includes: imageUrl, template +}); +``` + +### exit-intent:closed + +Fired when exit intent popup is closed: + +```javascript +next.on('exit-intent:closed', (data) => { + console.log('Exit intent closed:', data); + // data includes: imageUrl, template +}); +``` + +### exit-intent:action + +Fired when user takes an action on exit intent popup: + +```javascript +next.on('exit-intent:action', (data) => { + console.log('Exit intent action:', data); + // data includes: action, couponCode +}); +``` + +## Profile Events + +### profile:applied + +Fired when a profile is applied to the cart: + +```javascript +next.on('profile:applied', (data) => { + console.log('Profile applied:', data); + // data includes: profileId, previousProfileId, itemsSwapped, originalItems, cleared, profile +}); +``` + +### profile:reverted + +Fired when profile changes are reverted: + +```javascript +next.on('profile:reverted', (data) => { + console.log('Profile reverted:', data); + // data includes: previousProfileId, itemsRestored +}); +``` + +### profile:switched + +Fired when switching between profiles: + +```javascript +next.on('profile:switched', (data) => { + console.log('Profile switched:', data); + // data includes: fromProfileId, toProfileId, itemsAffected +}); +``` + +### profile:registered + +Fired when a new profile is registered: + +```javascript +next.on('profile:registered', (data) => { + console.log('Profile registered:', data); + // data includes: profileId, mappingsCount +}); +``` + +## System Events + +### route:changed + +Fired when SDK detects route/page change: + +```javascript +next.on('route:changed', (route) => { + console.log('Route changed:', route); + // route includes: path, params +}); +``` + +### sdk:route-invalidated + +Fired when SDK route context is invalidated: + +```javascript +next.on('sdk:route-invalidated', (data) => { + console.log('Route invalidated:', data); +}); +``` + +### page:viewed + +Fired when page view is tracked: + +```javascript +next.on('page:viewed', (data) => { + console.log('Page viewed:', data); + // data includes: page info, timestamp +}); +``` + +### error:occurred + +Fired when SDK encounters an error: + +```javascript +next.on('error:occurred', (error) => { + console.error('SDK error:', error); + // error includes: message, stack, context +}); +``` + +## Complete Event List + +Here are all 34 available events organized by category: + +### Cart (5 events) +- `cart:updated` - Cart state changed +- `cart:item-added` - Item added to cart +- `cart:item-removed` - Item removed from cart +- `cart:quantity-changed` - Item quantity changed +- `cart:package-swapped` - Package swapped for another + +### Checkout & Order (3 events) +- `checkout:started` - Checkout process began +- `checkout:form-initialized` - Checkout form ready +- `order:completed` - Order successfully completed + +### Payment (3 events) +- `payment:tokenized` - Payment token created +- `payment:error` - Payment processing failed +- `express-checkout:started` - Express checkout initiated + +### Campaign & Config (2 events) +- `campaign:loaded` - Campaign data loaded +- `config:updated` - Configuration changed + +### Coupons (2 events) +- `coupon:applied` - Discount code applied +- `coupon:removed` - Discount code removed + +### Upsells (4 events) +- `upsell:viewed` - Upsell offer shown +- `upsell:accepted` - Upsell accepted by user +- `upsell:added` - Upsell added to order +- `upsell:skipped` - Upsell declined/skipped + +### User (2 events) +- `user:logged-in` - User authenticated +- `user:logged-out` - User logged out + +### Behavioral (5 events) +- `fomo:shown` - FOMO popup displayed +- `exit-intent:clicked` - Exit intent popup clicked +- `exit-intent:dismissed` - Exit intent popup dismissed +- `exit-intent:closed` - Exit intent popup closed +- `exit-intent:action` - Exit intent action taken + +### Profile (4 events) +- `profile:applied` - Profile applied to cart +- `profile:reverted` - Profile changes reverted +- `profile:switched` - Switched between profiles +- `profile:registered` - New profile registered + +### System (5 events) +- `route:changed` - Page/route changed +- `sdk:route-invalidated` - Route context reset +- `page:viewed` - Page view tracked +- `error:occurred` - Error encountered + +## Event Handling Patterns + +### Basic Event Listening + +```javascript +// Subscribe to events +next.on('cart:updated', (data) => { + console.log('Cart updated:', data); +}); + +// Multiple events +['cart:item-added', 'cart:item-removed'].forEach(event => { + next.on(event, (data) => { + console.log(`Event ${event}:`, data); + }); +}); +``` + +### Removing Listeners + +```javascript +const handler = (data) => console.log(data); + +// Add listener +next.on('cart:updated', handler); + +// Remove listener +next.off('cart:updated', handler); +``` + +### Error Handling in Events + +```javascript +next.on('cart:updated', (data) => { + try { + // Your code here + updateUI(data); + } catch (error) { + console.error('Error in event handler:', error); + } +}); +``` + +### DOM Events vs SDK Events + +```javascript +// DOM events (for SDK lifecycle) +document.addEventListener('next:initialized', (event) => { + console.log('SDK ready via DOM event'); +}); + +// SDK events (for cart, checkout, etc.) +next.on('cart:updated', (data) => { + console.log('Cart updated via SDK event'); +}); +``` + +## Custom Event Integration + +### Google Analytics + +```javascript +next.on('cart:item-added', (data) => { + if (typeof gtag !== 'undefined') { + gtag('event', 'add_to_cart', { + value: data.item.price, + currency: 'USD', + items: [data.item] + }); + } +}); +``` + +### Custom Analytics + +```javascript +// Track all cart changes +next.on('cart:updated', (data) => { + fetch('/api/track', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + event: 'cart_updated', + properties: data + }) + }); +}); +``` + +## Best Practices + +1. **Always check data**: Validate event data before using +2. **Handle errors**: Wrap handlers in try-catch +3. **Clean up**: Remove listeners when no longer needed +4. **Don't block**: Keep handlers fast and async +5. **Test events**: Verify events fire as expected + +## Debugging Events + +### List All Available Events + +```javascript +// Get all registered events from the EventBus +if (window.next && window.next.eventBus) { + const listeners = window.next.eventBus.listeners; + const events = Array.from(listeners.keys()).sort(); + + console.log('Available events:', events.length); + events.forEach((event, i) => { + const count = listeners.get(event).size; + console.log(`${i + 1}. ${event} (${count} listeners)`); + }); +} +``` + +### Monitor All Events + +```javascript +// Log all events for debugging +if (window.location.search.includes('debug=true')) { + const allEvents = [ + // Cart + 'cart:updated', 'cart:item-added', 'cart:item-removed', 'cart:quantity-changed', 'cart:package-swapped', + // Checkout & Order + 'checkout:started', 'checkout:form-initialized', 'order:completed', + // Payment + 'payment:tokenized', 'payment:error', 'express-checkout:started', + // Campaign & Config + 'campaign:loaded', 'config:updated', + // Coupons + 'coupon:applied', 'coupon:removed', + // Upsells + 'upsell:viewed', 'upsell:accepted', 'upsell:added', 'upsell:skipped', + // User + 'user:logged-in', 'user:logged-out', + // Behavioral + 'fomo:shown', 'exit-intent:clicked', 'exit-intent:dismissed', 'exit-intent:closed', 'exit-intent:action', + // Profile + 'profile:applied', 'profile:reverted', 'profile:switched', 'profile:registered', + // System + 'route:changed', 'sdk:route-invalidated', 'page:viewed', 'error:occurred' + ]; + + allEvents.forEach(event => { + next.on(event, (data) => { + console.log(`[Event] ${event}:`, data); + }); + }); +} +``` \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/index.md b/docs/campaign-cart/api-reference/index.md new file mode 100644 index 0000000..fe2d94a --- /dev/null +++ b/docs/campaign-cart/api-reference/index.md @@ -0,0 +1,32 @@ +--- +sidebar_label: API Reference +sidebar_position: 1 +--- + +# API Reference + +Complete reference for JavaScript methods, events, and data attributes. + +## JavaScript API + +- [Methods](/docs/campaign-cart/api-reference/methods) - JavaScript API methods +- [Method Summary](/docs/campaign-cart/api-reference/method-summary) - Quick reference +- [Events](/docs/campaign-cart/api-reference/events) - SDK event system +- [Callbacks](/docs/campaign-cart/api-reference/callbacks) - Callback functions + +## Attributes & Styling + +- [Data Attributes](/docs/campaign-cart/api-reference/data-attributes) - Complete attribute reference +- [CSS Classes](/docs/campaign-cart/api-reference/css-classes) - Automatic CSS classes + +## Configuration + +- [Profiles](/docs/campaign-cart/api-reference/profiles) - Profile-based configuration +- [Attribution](/docs/campaign-cart/api-reference/attribution) - Attribution tracking +- [URL Parameters](/docs/campaign-cart/api-reference/url-parameters) - URL-based configuration + +## Related + +- [Getting Started](/docs/campaign-cart/getting-started/installation) - Setup and configuration +- [Examples](/docs/campaign-cart/examples/basic-product-page) - Usage examples + diff --git a/docs/campaign-cart/api-reference/method-summary.md b/docs/campaign-cart/api-reference/method-summary.md new file mode 100644 index 0000000..3350bf5 --- /dev/null +++ b/docs/campaign-cart/api-reference/method-summary.md @@ -0,0 +1,178 @@ +# NextCommerce JavaScript API Summary + +This document provides a quick reference of all available methods in the NextCommerce SDK (`window.next`). + +## Production API Methods + +### Cart Management +**Note:** Cart methods that accept parameters require an options object, not direct values. + +- `addItem({ packageId, quantity? })` - Add item to cart *(not `addItem(123)`)* +- `removeItem({ packageId })` - Remove item from cart *(not `removeItem(123)`)* +- `updateQuantity({ packageId, quantity })` - Update item quantity *(not `updateQuantity(123, 5)`)* +- `clearCart()` - Remove all items +- `hasItemInCart({ packageId })` - Check if item exists *(not `hasItemInCart(123)`)* +- `getCartData()` - Get comprehensive cart data +- `getCartTotals()` - Get cart pricing totals +- `getCartCount()` - Get total item count + +### Campaign & Package Data +- `getCampaignData()` - Get campaign configuration +- `getPackage(id)` - Get specific package details + +### Coupons +- `applyCoupon(code)` - Apply discount code +- `removeCoupon(code)` - Remove discount code +- `getCoupons()` - Get applied coupons +- `validateCoupon(code)` - Validate without applying +- `calculateDiscountAmount(coupon)` - Calculate discount amount + +### Shipping +- `getShippingMethods()` - Get available methods +- `getSelectedShippingMethod()` - Get current selection +- `setShippingMethod(methodId)` - Set shipping method + +### Tracking & Analytics +- `trackViewItemList(packageIds, listId?, listName?)` - Track list views +- `trackViewItem(packageId)` - Track single item view +- `trackAddToCart(packageId, quantity?)` - Track add to cart +- `trackRemoveFromCart(packageId, quantity?)` - Track removal +- `trackBeginCheckout()` - Track checkout start +- `trackPurchase(orderData)` - Track purchase completion +- `trackCustomEvent(eventName, data?)` - Track custom events +- `trackSignUp(email)` - Track user signup +- `trackLogin(email)` - Track user login +- `setDebugMode(enabled)` - Enable analytics debugging +- `invalidateAnalyticsContext()` - Reset analytics context + +### Upsells (Post-Purchase) +- `addUpsell({ packageId?, quantity?, items? })` - Add upsell to order +- `canAddUpsells()` - Check if upsells available +- `getCompletedUpsells()` - Get added upsell IDs +- `isUpsellAlreadyAdded(packageId)` - Check if already added + +### Behavioral Features +- `fomo(config?)` - Start FOMO popups +- `stopFomo()` - Stop FOMO popups +- `exitIntent({ image, action? })` - Show exit intent popup +- `disableExitIntent()` - Disable exit intent + +### Events +- `on(event, handler)` - Subscribe to events +- `off(event, handler)` - Unsubscribe from events + +### Callbacks (Legacy) +- `registerCallback(type, callback)` - Register lifecycle callback +- `unregisterCallback(type, callback)` - Remove lifecycle callback +- Available callback types: + - `beforeRender`, `afterRender` + - `beforeCheckout`, `afterCheckout` + - `beforeRedirect` + - `itemAdded`, `itemRemoved`, `cartCleared` + +### Utilities +- `formatPrice(amount, currency?)` - Format price display +- `validateCheckout()` - Validate cart for checkout + +## Debug API Methods (`window.nextDebug`) + +Available when `?debugger=true` is in URL. + +### Store Access +- `stores.cart` - Cart store access +- `stores.campaign` - Campaign store access +- `stores.config` - Config store access +- `stores.checkout` - Checkout store access +- `stores.order` - Order store access +- `stores.attribution` - Attribution store access + +### Cart Testing +- `addToCart(packageId, quantity?)` - Quick add to cart +- `removeFromCart(packageId)` - Quick remove +- `updateQuantity(packageId, quantity)` - Quick update +- `addTestItems()` - Add preset test items + +### Campaign Tools +- `loadCampaign()` - Reload campaign data +- `clearCampaignCache()` - Clear cache +- `getCacheInfo()` - Get cache details +- `inspectPackage(packageId)` - Inspect package +- `testShippingMethod(methodId)` - Test shipping + +### Analytics Debug +- `analytics.getStatus()` - Get analytics status +- `analytics.getProviders()` - Get loaded providers +- `analytics.track(name, data)` - Track test event +- `analytics.setDebugMode(enabled)` - Toggle debug mode +- `analytics.invalidateContext()` - Reset context + +### Attribution Debug +- `attribution.debug()` - Show attribution data +- `attribution.get()` - Get attribution for API +- `attribution.setFunnel(name)` - Set funnel name +- `attribution.setEvclid(id)` - Set Everflow ID +- `attribution.getFunnel()` - Get current funnel +- `attribution.clearFunnel()` - Clear funnel + +### System Tools +- `sdk` - Direct SDK instance access +- `getStats()` - Get initialization stats +- `reinitialize()` - Reinitialize SDK +- `testMode` - Test mode controls +- `overlay` - Debug overlay controls + +## Event Types + +### Cart Events +- `cart:updated` - Cart state changed +- `cart:item-added` - Item added +- `cart:item-removed` - Item removed +- `cart:quantity-changed` - Quantity updated + +### Checkout & Order Events +- `checkout:started` - Checkout began +- `checkout:form-initialized` - Form ready +- `order:completed` - Order finished +- `payment:tokenized` - Payment tokenized +- `payment:error` - Payment failed + +### Coupon Events +- `coupon:applied` - Coupon applied +- `coupon:removed` - Coupon removed + +### Upsell Events +- `upsell:viewed` - Upsell displayed +- `upsell:accepted` - Upsell accepted +- `upsell:added` - Upsell added to order +- `upsell:skipped` - Upsell skipped + +### Other Events +- `campaign:loaded` - Campaign data loaded +- `config:updated` - Config changed +- `error:occurred` - Error happened +- `fomo:shown` - FOMO popup shown +- `route:changed` - Route changed + +## Initialization System + +### Ready Queue +```javascript +// Queue callbacks before SDK loads +window.nextReady = window.nextReady || []; +window.nextReady.push(function() { + // SDK is ready +}); +``` + +### DOM Events + +Listen on `document`: +- `next:ready` - SDK module loaded (early) +- `next:initialized` - SDK fully initialized (recommended) +- `next:cart-updated` - Cart updated +- `next:item-added` - Item added +- `next:item-removed` - Item removed +- `next:checkout-started` - Checkout started +- `next:payment-success` - Payment succeeded +- `next:payment-error` - Payment failed +- `next:timer-expired` - Timer expired \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/methods.md b/docs/campaign-cart/api-reference/methods.md new file mode 100644 index 0000000..0561ce8 --- /dev/null +++ b/docs/campaign-cart/api-reference/methods.md @@ -0,0 +1,1130 @@ +# JavaScript API Methods + +The Next Commerce JS SDK exposes methods through the `window.next` object after initialization. + +## Table of Contents + +- [Getting Started](#getting-started) +- [Cart Methods](#cart-methods) +- [Campaign & Package Methods](#campaign--package-methods) +- [Profile Methods](#profile-methods) +- [Coupon Methods](#coupon-methods) +- [Tracking & Analytics Methods](#tracking--analytics-methods) +- [Shipping Methods](#shipping-methods) +- [Upsell Methods](#upsell-methods) +- [Event Methods](#event-methods) +- [Behavioral Methods](#behavioral-methods) +- [Utility Methods](#utility-methods) +- [Debug API](#debug-api) + +## Getting Started + +### Initialization Detection + +```javascript +// Check if SDK is ready +if (window.next) { + // SDK is ready, use it directly + next.addItem({ packageId: 1 }); +} else { + // Queue for later execution + window.nextReady = window.nextReady || []; + window.nextReady.push(function() { + next.addItem({ packageId: 1 }); + }); +} + +// Or listen for initialization event +document.addEventListener('next:initialized', function(event) { + // SDK is ready + console.log('SDK initialized at', new Date(event.detail.timestamp)); + next.addItem({ packageId: 1 }); +}); +``` + +## Cart Methods + +### addItem + +Adds an item to the cart. **Note: This method requires an options object, not a direct package ID.** + +```javascript +// ✅ CORRECT - Pass an object with packageId property +await next.addItem({ + packageId: 123, + quantity: 2 +}); + +// ✅ CORRECT - Quantity is optional (defaults to 1) +await next.addItem({ packageId: 123 }); + +// ❌ WRONG - Don't pass packageId directly +// next.addItem(123); // This won't work! +``` + +**Parameters:** +- `options` (object): Required options object + - `options.packageId` (number): Package ID to add + - `options.quantity` (number, optional): Quantity to add (default: 1) + +**Returns:** `Promise<void>` + +### removeItem + +Removes an item from the cart. **Requires an options object.** + +```javascript +// ✅ CORRECT +await next.removeItem({ packageId: 123 }); + +// ❌ WRONG +// next.removeItem(123); // This won't work! +``` + +**Parameters:** +- `options` (object): Required options object + - `options.packageId` (number): Package ID to remove + +**Returns:** `Promise<void>` + +### updateQuantity + +Updates the quantity of an item in the cart. **Requires an options object.** + +```javascript +// ✅ CORRECT +await next.updateQuantity({ + packageId: 123, + quantity: 5 +}); + +// ❌ WRONG +// next.updateQuantity(123, 5); // This won't work! +``` + +**Parameters:** +- `options` (object): Required options object + - `options.packageId` (number): Package ID to update + - `options.quantity` (number): New quantity + +**Returns:** `Promise<void>` + +### clearCart + +Removes all items from the cart. + +```javascript +await next.clearCart(); +``` + +**Returns:** `Promise<void>` + +### hasItemInCart + +Checks if an item is in the cart. **Requires an options object.** + +```javascript +// ✅ CORRECT +const hasItem = next.hasItemInCart({ packageId: 123 }); +console.log('Item in cart:', hasItem); // true or false + +// ❌ WRONG +// next.hasItemInCart(123); // This won't work! +``` + +**Parameters:** +- `options` (object): Required options object + - `options.packageId` (number): Package ID to check + +**Returns:** `boolean` + +### getCartData + +Returns comprehensive cart data including enriched items, totals, campaign data, and applied coupons. + +```javascript +const cartData = next.getCartData(); +console.log(cartData); +// { +// cartLines: [...], +// cartTotals: { subtotal: {...}, total: {...}, ... }, +// campaignData: {...}, +// appliedCoupons: [...] +// } +``` + +**Returns:** +```typescript +{ + cartLines: EnrichedCartLine[]; + cartTotals: CartTotals; + campaignData: Campaign | null; + appliedCoupons: AppliedCoupon[]; +} +``` + +### getCartTotals + +Returns cart totals and pricing information. + +```javascript +const totals = next.getCartTotals(); +console.log('Total:', totals.total.formatted); // "$99.99" +console.log('Subtotal:', totals.subtotal.formatted); // "$89.99" +console.log('Shipping:', totals.shipping.formatted); // "$10.00" +``` + +**Returns:** `CartTotals` object with subtotal, shipping, tax, discounts, and total + +### getCartCount + +Returns the total number of items in the cart. + +```javascript +const count = next.getCartCount(); +console.log('Items in cart:', count); // 3 +``` + +**Returns:** `number` + +## Campaign & Package Methods + +### getCampaignData + +Returns the loaded campaign data. + +```javascript +const campaign = next.getCampaignData(); +if (campaign) { + console.log('Campaign:', campaign.name); + console.log('Currency:', campaign.currency); + console.log('Packages:', campaign.packages); +} +``` + +**Returns:** `Campaign | null` + +### getPackage + +Gets detailed information about a specific package. + +```javascript +const package = next.getPackage(123); +if (package) { + console.log('Package name:', package.display_name); + console.log('Price:', next.formatPrice(package.price)); +} +``` + +**Parameters:** +- `id` (number): Package ID + +**Returns:** `Package | null` + +## Profile Methods + +Profile methods enable dynamic package ID mapping for different pricing tiers, promotions, and customer segments. See the [complete Profile API documentation](./profiles.md) for detailed information. + +### setProfile + +Applies a pricing profile to swap package IDs. + +```javascript +// Basic usage +await next.setProfile('black_friday'); + +// With options +await next.setProfile('vip', { + clearCart: true, + preserveQuantities: false +}); +``` + +### revertProfile + +Reverts to original packages. + +```javascript +await next.revertProfile(); +``` + +### getActiveProfile + +Gets the currently active profile ID. + +```javascript +const profile = next.getActiveProfile(); +console.log(profile); // "black_friday" or null +``` + +### getMappedPackageId + +Gets the mapped package ID for the active profile. + +```javascript +const mappedId = next.getMappedPackageId(1); +console.log(mappedId); // 101 (if profile maps 1 -> 101) +``` + +### listProfiles + +Lists all configured profiles. + +```javascript +const profiles = next.listProfiles(); +console.log(profiles); // ["regular", "black_friday", "vip"] +``` + +See [Profile API Reference](./profiles.md) for complete documentation including events, data attributes, and configuration. + +## Coupon Methods + +### applyCoupon + +Applies a coupon code to the cart. + +```javascript +const result = await next.applyCoupon('SAVE20'); +if (result.success) { + console.log('Coupon applied:', result.message); + console.log('Discount amount:', result.data.amount); +} else { + console.error('Coupon error:', result.message); +} +``` + +**Parameters:** +- `code` (string): Coupon code to apply + +**Returns:** +```typescript +Promise<{ + success: boolean; + message: string; + data?: { amount: number; formatted: string; } +}> +``` + +### removeCoupon + +Removes a coupon from the cart. + +```javascript +next.removeCoupon('SAVE20'); +``` + +**Parameters:** +- `code` (string): Coupon code to remove + +**Returns:** `void` + +### getCoupons + +Returns all applied coupons. + +```javascript +const coupons = next.getCoupons(); +coupons.forEach(coupon => { + console.log(`${coupon.code}: ${coupon.amount.formatted} off`); +}); +``` + +**Returns:** `AppliedCoupon[]` + +### validateCoupon + +Validates a coupon without applying it. + +```javascript +const validation = next.validateCoupon('TESTCODE'); +if (validation.valid) { + console.log('Coupon is valid'); +} else { + console.log('Invalid:', validation.message); +} +``` + +**Parameters:** +- `code` (string): Coupon code to validate + +**Returns:** +```typescript +{ + valid: boolean; + message?: string; +} +``` + +### calculateDiscountAmount + +Calculates the discount amount for a given coupon definition. + +```javascript +const amount = next.calculateDiscountAmount(couponDefinition); +console.log('Discount amount:', amount); +``` + +**Parameters:** +- `coupon` (DiscountDefinition): Coupon definition object + +**Returns:** `number` - Calculated discount amount + +## Tracking & Analytics Methods + +### trackViewItemList + +Tracks when users view a list of products. + +```javascript +// Basic tracking +await next.trackViewItemList(['1', '2', '3']); + +// With list context +await next.trackViewItemList( + ['1', '2', '3'], + 'homepage', + 'Featured Products' +); +``` + +**Parameters:** +- `packageIds` (Array<string|number>): Array of package IDs +- `listId` (string, optional): Unique list identifier +- `listName` (string, optional): Human-readable list name + +**Returns:** `Promise<void>` + +### trackViewItem + +Tracks when a single item is viewed. + +```javascript +await next.trackViewItem('1'); +``` + +**Parameters:** +- `packageId` (string|number): Package ID viewed + +**Returns:** `Promise<void>` + +### trackAddToCart + +Tracks when an item is added to cart. + +```javascript +await next.trackAddToCart('1', 2); +``` + +**Parameters:** +- `packageId` (string|number): Package ID added +- `quantity` (number, optional): Quantity added (default: 1) + +**Returns:** `Promise<void>` + +### trackRemoveFromCart + +Tracks when an item is removed from cart. + +```javascript +await next.trackRemoveFromCart('1', 1); +``` + +**Parameters:** +- `packageId` (string|number): Package ID removed +- `quantity` (number, optional): Quantity removed (default: 1) + +**Returns:** `Promise<void>` + +### trackBeginCheckout + +Tracks when checkout process begins. + +```javascript +await next.trackBeginCheckout(); +``` + +**Returns:** `Promise<void>` + +### trackPurchase + +Tracks completed purchases. + +```javascript +// Track with order data +await next.trackPurchase(orderData); +``` + +**Parameters:** +- `orderData` (any): Order data object + +**Returns:** `Promise<void>` + +### trackCustomEvent + +Tracks custom events with optional data. + +```javascript +// Simple custom event +await next.trackCustomEvent('video_played'); + +// Custom event with data +await next.trackCustomEvent('user_engagement', { + section: 'hero', + action: 'video_play', + duration: 30 +}); +``` + +**Parameters:** +- `eventName` (string): Custom event name +- `data` (Record<string, any>, optional): Additional event data + +**Returns:** `Promise<void>` + +### trackSignUp + +Tracks user sign-up events. + +```javascript +await next.trackSignUp('user@example.com'); +``` + +**Parameters:** +- `email` (string): User's email address + +**Returns:** `Promise<void>` + +### trackLogin + +Tracks user login events. + +```javascript +await next.trackLogin('user@example.com'); +``` + +**Parameters:** +- `email` (string): User's email address + +**Returns:** `Promise<void>` + +### setDebugMode (Analytics) + +Enables or disables analytics debug mode. + +```javascript +await next.setDebugMode(true); // Enable debug logging +``` + +**Parameters:** +- `enabled` (boolean): Enable or disable debug mode + +**Returns:** `Promise<void>` + +### invalidateAnalyticsContext + +Invalidates the analytics context, useful when switching between pages. + +```javascript +await next.invalidateAnalyticsContext(); +``` + +**Returns:** `Promise<void>` + +## Shipping Methods + +### getShippingMethods + +Returns all available shipping methods from the campaign. + +```javascript +const methods = next.getShippingMethods(); +console.log(methods); +// [ +// {ref_id: 1, code: "standard", price: "0.00"}, +// {ref_id: 2, code: "express", price: "12.99"} +// ] +``` + +**Returns:** `Array<{ref_id: number; code: string; price: string}>` + +### getSelectedShippingMethod + +Returns the currently selected shipping method. + +```javascript +const selected = next.getSelectedShippingMethod(); +if (selected) { + console.log('Shipping:', selected.name, selected.price); +} +``` + +**Returns:** `{id: number; name: string; price: number; code: string} | null` + +### setShippingMethod + +Sets the shipping method by ID. + +```javascript +// Set standard shipping (ID 1) +await next.setShippingMethod(1); + +// Set express shipping (ID 2) +await next.setShippingMethod(2); +``` + +**Parameters:** +- `methodId` (number): The ref_id of the shipping method from campaign data + +**Returns:** `Promise<void>` + +**Throws:** Error if shipping method ID is not found in campaign data + +## Upsell Methods + +### addUpsell + +Adds upsell items to a completed order. Only available after order completion. + +```javascript +// Add single upsell +const result = await next.addUpsell({ + packageId: 123, + quantity: 1 +}); + +// Add multiple upsells at once +const result = await next.addUpsell({ + items: [ + { packageId: 123, quantity: 1 }, + { packageId: 456, quantity: 2 } + ] +}); + +console.log('Upsells added:', result.addedLines); +console.log('Total upsell value:', result.totalValue); +``` + +**Parameters:** +- `options.packageId` (number, optional): Single package ID to add +- `options.quantity` (number, optional): Quantity for single item (default: 1) +- `options.items` (Array, optional): Multiple items to add + - `items[].packageId` (number): Package ID + - `items[].quantity` (number, optional): Quantity (default: 1) + +**Returns:** +```typescript +Promise<{ + order: Order; + addedLines: OrderLine[]; + totalValue: number; +}> +``` + +**Throws:** Error if no order exists or order doesn't support upsells + +### canAddUpsells + +Checks if upsells can be added to the current order. + +```javascript +if (next.canAddUpsells()) { + console.log('Order supports upsells'); +} +``` + +**Returns:** `boolean` + +### getCompletedUpsells + +Returns array of package IDs that have been added as upsells. + +```javascript +const completedUpsells = next.getCompletedUpsells(); +console.log('Upsells added:', completedUpsells); // ['123', '456'] +``` + +**Returns:** `string[]` + +### isUpsellAlreadyAdded + +Checks if a specific package has already been added as an upsell. + +```javascript +if (next.isUpsellAlreadyAdded(123)) { + console.log('This upsell was already added'); +} +``` + +**Parameters:** +- `packageId` (number): Package ID to check + +**Returns:** `boolean` + +## Behavioral Methods + +### fomo + +Starts FOMO (Fear of Missing Out) popup notifications showing recent purchases. + +```javascript +// Start with default configuration +await next.fomo(); + +// Start with custom configuration +await next.fomo({ + items: [ + { text: "Premium Package", image: "/images/product1.jpg" }, + { text: "Starter Bundle", image: "/images/product2.jpg" } + ], + customers: { + US: ["John", "Sarah", "Mike", "Emma"], + GB: ["James", "Sophie", "Oliver", "Lily"] + }, + maxMobileShows: 2, + displayDuration: 5000, + delayBetween: 12000, + initialDelay: 3000 +}); +``` + +**Parameters:** +- `config` (object, optional): + - `items` (Array): Custom product items to show + - `customers` (object): Customer names by country code + - `maxMobileShows` (number): Max popups on mobile (default: 2) + - `displayDuration` (number): How long to show each popup in ms (default: 5000) + - `delayBetween` (number): Delay between popups in ms (default: 12000) + - `initialDelay` (number): Initial delay before first popup in ms (default: 3000) + +**Returns:** `Promise<void>` + +### stopFomo + +Stops FOMO popup notifications. + +```javascript +next.stopFomo(); +``` + +**Returns:** `void` + +### exitIntent + +Shows exit intent popup when user tries to leave the page. + +```javascript +await next.exitIntent({ + image: 'https://example.com/special-offer.jpg', + action: async () => { + // Apply discount when popup is clicked + await next.applyCoupon('EXIT20'); + // Close the popup + } +}); +``` + +**Parameters:** +- `options.image` (string): URL of the image to display +- `options.action` (function, optional): Function to call when popup is clicked + +**Returns:** `Promise<void>` + +### disableExitIntent + +Disables the exit intent popup. + +```javascript +next.disableExitIntent(); +``` + +**Returns:** `void` + +## Event Methods + +### on + +Subscribe to internal SDK events. + +```javascript +// Listen for cart updates +next.on('cart:updated', (cartState) => { + console.log('Cart updated:', cartState.items.length, 'items'); +}); + +// Listen for item additions +next.on('cart:item-added', (data) => { + console.log('Item added:', data.packageId); +}); +``` + +**Parameters:** +- `event` (string): Event name from EventMap +- `handler` (function): Event handler function + +**Returns:** `void` + +### off + +Unsubscribe from internal SDK events. + +```javascript +const handler = (data) => console.log(data); +next.on('cart:updated', handler); +// Later... +next.off('cart:updated', handler); +``` + +**Parameters:** +- `event` (string): Event name +- `handler` (function): Handler function to remove + +**Returns:** `void` + +### Available Events + +```javascript +// Cart events +next.on('cart:updated', (cartState) => { /* ... */ }); +next.on('cart:item-added', (data) => { /* ... */ }); +next.on('cart:item-removed', (data) => { /* ... */ }); +next.on('cart:quantity-changed', (data) => { /* ... */ }); + +// Campaign events +next.on('campaign:loaded', (campaign) => { /* ... */ }); + +// Checkout events +next.on('checkout:started', (data) => { /* ... */ }); +next.on('checkout:form-initialized', () => { /* ... */ }); + +// Order events +next.on('order:completed', (order) => { /* ... */ }); + +// Payment events +next.on('payment:tokenized', (data) => { /* ... */ }); +next.on('payment:error', (error) => { /* ... */ }); + +// Coupon events +next.on('coupon:applied', (coupon) => { /* ... */ }); +next.on('coupon:removed', (code) => { /* ... */ }); + +// Upsell events +next.on('upsell:added', (data) => { /* ... */ }); +next.on('upsell:skipped', (data) => { /* ... */ }); +next.on('upsell:viewed', (data) => { /* ... */ }); +next.on('upsell:accepted', (data) => { /* ... */ }); + +// Other events +next.on('error:occurred', (error) => { /* ... */ }); +next.on('config:updated', (config) => { /* ... */ }); +next.on('fomo:shown', (data) => { /* ... */ }); +next.on('route:changed', (route) => { /* ... */ }); +``` + +## Utility Methods + +### formatPrice + +Formats a price value according to campaign currency. + +```javascript +const formatted = next.formatPrice(19.99); // "$19.99" +const euros = next.formatPrice(19.99, 'EUR'); // "€19.99" +``` + +**Parameters:** +- `amount` (number): Price amount to format +- `currency` (string, optional): Currency code (uses campaign currency if not provided) + +**Returns:** `string` + +### validateCheckout + +Validates if the cart is ready for checkout. + +```javascript +const validation = next.validateCheckout(); +if (!validation.valid) { + console.error('Cannot checkout:', validation.errors); +} +``` + +**Returns:** +```typescript +{ + valid: boolean; + errors?: string[]; +} +``` + +### registerCallback + +Register a callback for specific SDK operations (legacy compatibility). + +```javascript +next.registerCallback('itemAdded', (data) => { + console.log('Item added:', data); + // data includes: cartLines, cartTotals, campaignData, appliedCoupons +}); +``` + +**Parameters:** +- `type` (CallbackType): Type of callback. Available types: + - `'beforeRender'` - Before cart UI renders + - `'afterRender'` - After cart UI renders + - `'beforeCheckout'` - Before checkout starts + - `'afterCheckout'` - After checkout completes + - `'beforeRedirect'` - Before order redirect + - `'itemAdded'` - After item added to cart + - `'itemRemoved'` - After item removed from cart + - `'cartCleared'` - After cart cleared +- `callback` (function): Callback function that receives `CallbackData` + +**Returns:** `void` + +### unregisterCallback + +Remove a previously registered callback. + +```javascript +const myCallback = (data) => console.log(data); +next.registerCallback('itemAdded', myCallback); +// Later... +next.unregisterCallback('itemAdded', myCallback); +``` + +**Parameters:** +- `type` (CallbackType): Type of callback +- `callback` (function): The exact callback function to remove + +**Returns:** `void` + +### triggerCallback (Internal) + +Triggers all callbacks of a specific type. This is used internally by the SDK. + +**Note:** This method is for internal SDK use only and should not be called directly. + +## Debug API + +The debug API provides powerful utilities for development and troubleshooting. Available only when debug mode is enabled with `?debugger=true`. + +### Accessing Debug Mode + +```javascript +// Check if debug mode is available +if (window.nextDebug) { + console.log('Debug mode available'); +} +``` + +### Store Access + +Direct access to all internal stores: + +```javascript +// Cart store +const cartState = nextDebug.stores.cart.getState(); +console.log('Cart items:', cartState.items); + +// Campaign store +const campaignState = nextDebug.stores.campaign.getState(); +console.log('Campaign data:', campaignState.data); + +// Config store +const configState = nextDebug.stores.config.getState(); +console.log('API key:', configState.apiKey); + +// Checkout store +const checkoutState = nextDebug.stores.checkout.getState(); + +// Order store +const orderState = nextDebug.stores.order.getState(); + +// Attribution store +const attributionState = nextDebug.stores.attribution.getState(); +``` + +### Cart Debug Methods + +```javascript +// Quick cart operations +nextDebug.addToCart(123); // Add single item +nextDebug.addToCart(123, 3); // Add with quantity +nextDebug.removeFromCart(123); // Remove item +nextDebug.updateQuantity(123, 5); // Update quantity + +// Add test items (packages 2, 7, 9) +nextDebug.addTestItems(); +``` + +### Campaign Debug Methods + +```javascript +// Reload campaign data +await nextDebug.loadCampaign(); + +// Clear campaign cache +nextDebug.clearCampaignCache(); + +// Get cache information +const cacheInfo = nextDebug.getCacheInfo(); + +// Inspect specific package +nextDebug.inspectPackage(123); + +// Test shipping methods +await nextDebug.testShippingMethod(1); +``` + +### Analytics Debug Methods + +```javascript +// Get analytics status +const status = await nextDebug.analytics.getStatus(); + +// Get loaded providers +const providers = await nextDebug.analytics.getProviders(); + +// Track test event +await nextDebug.analytics.track('test_event', { test: true }); + +// Enable analytics debug mode +await nextDebug.analytics.setDebugMode(true); + +// Invalidate analytics context +await nextDebug.analytics.invalidateContext(); +``` + +### Attribution Debug Methods + +```javascript +// Debug attribution data +nextDebug.attribution.debug(); + +// Get attribution for API +const attribution = nextDebug.attribution.get(); + +// Set funnel name +nextDebug.attribution.setFunnel('debug-funnel'); + +// Set Everflow click ID +nextDebug.attribution.setEvclid('test-evclid-123'); + +// Get current funnel +const funnel = nextDebug.attribution.getFunnel(); + +// Clear persisted funnel +nextDebug.attribution.clearFunnel(); +``` + +### System Debug Methods + +```javascript +// Direct SDK access +nextDebug.sdk.addItem({ packageId: 123 }); + +// Get initialization stats +const stats = nextDebug.getStats(); + +// Reinitialize SDK +await nextDebug.reinitialize(); + +// Test mode manager +nextDebug.testMode.enable(); +nextDebug.testMode.disable(); +nextDebug.testMode.getConfig(); +``` + +### Debug Overlay Control + +```javascript +// Show/hide debug overlay +nextDebug.overlay.show(); +nextDebug.overlay.hide(); +nextDebug.overlay.toggle(); + +// Check visibility +const isVisible = nextDebug.overlay.isVisible(); +``` + +## Error Handling + +### Standard Error Handling + +```javascript +try { + await next.addItem({ packageId: 123, quantity: 2 }); + console.log('Item added successfully'); +} catch (error) { + console.error('Failed to add item:', error.message); +} +``` + +### Async Method Handling + +```javascript +// Most cart/coupon methods are async +const result = await next.applyCoupon('INVALID_CODE'); +if (!result.success) { + console.error('Coupon error:', result.message); + // Show user-friendly message +} +``` + +### Event Error Handling + +```javascript +next.on('error:occurred', (error) => { + console.error('SDK error:', error); + // Handle errors gracefully +}); +``` + +## Best Practices + +1. **Always wait for SDK initialization** before calling methods +2. **Use async/await** for cleaner code with promises +3. **Handle errors appropriately** - check success flags and catch exceptions +4. **Subscribe to events** for reactive UI updates +5. **Use debug mode** during development for troubleshooting +6. **Batch operations** when possible for better performance +7. **Validate inputs** before calling methods +8. **Clean up event listeners** when no longer needed + +## Common Patterns + +### E-commerce Flow + +```javascript +window.nextReady.push(async function() { + // 1. Track product list view + await next.trackViewItemList(['1', '2', '3'], 'category', 'Best Sellers'); + + // 2. Add item to cart + await next.addItem({ packageId: 1, quantity: 2 }); + + // 3. Apply coupon + const couponResult = await next.applyCoupon('SAVE20'); + if (couponResult.success) { + console.log('Discount applied'); + } + + // 4. Track checkout start + await next.trackBeginCheckout(); +}); +``` + +### Reactive Cart Updates + +```javascript +// Update UI when cart changes +next.on('cart:updated', (cartState) => { + document.querySelector('.cart-count').textContent = next.getCartCount(); + document.querySelector('.cart-total').textContent = cartState.totals.total.formatted; +}); + +// React to specific actions +next.on('cart:item-added', (data) => { + showNotification(`${data.packageName} added to cart`); +}); +``` \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/profiles.md b/docs/campaign-cart/api-reference/profiles.md new file mode 100644 index 0000000..de2828a --- /dev/null +++ b/docs/campaign-cart/api-reference/profiles.md @@ -0,0 +1,354 @@ +# Profile API Reference + +The Profile System provides methods for managing dynamic package mappings through the `window.next` API. + +## Methods + +### setProfile(profileId, options?) + +Applies a profile to the current session, swapping all package IDs according to the profile's mappings. + +**Parameters:** +- `profileId` (string, required): The ID of the profile to apply +- `options` (object, optional): + - `clearCart` (boolean): Clear cart before applying profile (default: false) + - `preserveQuantities` (boolean): Maintain item quantities (default: true) + +**Returns:** Promise<void> + +**Example:** +```javascript +// Basic usage +await window.next.setProfile('black_friday'); + +// With options +await window.next.setProfile('vip', { + clearCart: true, + preserveQuantities: false +}); +``` + +### revertProfile() + +Reverts to the original cart state before any profile was applied. + +**Returns:** Promise<void> + +**Example:** +```javascript +await window.next.revertProfile(); +``` + +### getActiveProfile() + +Returns the ID of the currently active profile. + +**Returns:** string | null + +**Example:** +```javascript +const currentProfile = window.next.getActiveProfile(); +console.log(currentProfile); // "black_friday" or null +``` + +### getProfileInfo(profileId?) + +Gets detailed information about a profile. + +**Parameters:** +- `profileId` (string, optional): Profile ID to query. If omitted, returns active profile info. + +**Returns:** Profile object or null + +**Example:** +```javascript +const profile = window.next.getProfileInfo('black_friday'); +console.log(profile); +// { +// id: "black_friday", +// name: "Black Friday Sale", +// packageMappings: { 1: 101, 2: 102, ... } +// } +``` + +### getMappedPackageId(originalId) + +Gets the mapped package ID for the active profile. + +**Parameters:** +- `originalId` (number): The original package ID + +**Returns:** number (mapped ID or original if no mapping) + +**Example:** +```javascript +const mappedId = window.next.getMappedPackageId(1); +console.log(mappedId); // 101 (if black_friday profile is active) +``` + +### getOriginalPackageId(mappedId) + +Gets the original package ID from a mapped ID. + +**Parameters:** +- `mappedId` (number): The mapped package ID + +**Returns:** number | null + +**Example:** +```javascript +const originalId = window.next.getOriginalPackageId(101); +console.log(originalId); // 1 +``` + +### listProfiles() + +Returns an array of all configured profile IDs. + +**Returns:** string[] + +**Example:** +```javascript +const profiles = window.next.listProfiles(); +console.log(profiles); // ["regular", "black_friday", "vip"] +``` + +### hasProfile(profileId) + +Checks if a profile exists in the configuration. + +**Parameters:** +- `profileId` (string): Profile ID to check + +**Returns:** boolean + +**Example:** +```javascript +if (window.next.hasProfile('black_friday')) { + // Profile exists +} +``` + +### registerProfile(profile) + +Registers a new profile dynamically at runtime. + +**Parameters:** +- `profile` (object): + - `id` (string): Unique profile identifier + - `name` (string): Display name + - `description` (string, optional): Profile description + - `packageMappings` (object): Package ID mappings + +**Returns:** void + +**Example:** +```javascript +window.next.registerProfile({ + id: 'flash_sale', + name: 'Flash Sale', + description: '2-hour flash sale', + packageMappings: { + 1: 401, + 2: 402, + 3: 403 + } +}); +``` + +## Events + +### profile:applied + +Fired when a profile is successfully applied. + +**Event Data:** +- `profileId` (string): Applied profile ID +- `previousProfileId` (string | null): Previous profile ID +- `itemsSwapped` (number): Number of cart items affected +- `originalItems` (number): Original cart item count +- `cleared` (boolean): Whether cart was cleared +- `profile` (object): Full profile object + +**Example:** +```javascript +window.next.on('profile:applied', (data) => { + console.log(`Profile ${data.profileId} applied`); + console.log(`Swapped ${data.itemsSwapped} items`); +}); +``` + +### profile:reverted + +Fired when a profile is reverted. + +**Event Data:** +- `previousProfileId` (string | null): The profile that was active +- `itemsRestored` (number): Number of items restored + +**Example:** +```javascript +window.next.on('profile:reverted', (data) => { + console.log(`Restored ${data.itemsRestored} original items`); +}); +``` + +### profile:switched + +Fired when switching between profiles. + +**Event Data:** +- `fromProfileId` (string | null): Previous profile +- `toProfileId` (string): New profile +- `itemsAffected` (number): Number of items affected + +### profile:registered + +Fired when a new profile is registered. + +**Event Data:** +- `profileId` (string): Registered profile ID +- `mappingsCount` (number): Number of package mappings + +## Data Attributes + +### data-next-profile + +Activates a profile when clicked. + +**Attributes:** +- `data-next-profile` (required): Profile ID to activate +- `data-next-clear-cart` (optional): Clear cart before applying ("true"/"false") +- `data-next-preserve-quantities` (optional): Preserve item quantities ("true"/"false") +- `data-next-active-text` (optional): Text to show when profile is active +- `data-next-inactive-text` (optional): Text to show when profile is inactive + +**Example:** +```html + +``` + +### data-next-profile-selector + +Creates a dropdown selector for profiles. + +**Attributes:** +- `data-next-profile-selector` (required): Marks element as profile selector +- `data-next-auto-populate` (optional): Auto-populate with configured profiles ("true") +- `data-next-clear-cart` (optional): Clear cart on profile change ("true"/"false") +- `data-next-preserve-quantities` (optional): Preserve quantities ("true"/"false") + +**Example:** +```html + +``` + +### data-next-show-if-profile + +Shows element only when specific profile is active. + +**Attributes:** +- `data-next-show-if-profile` (required): Profile ID to check + +**Example:** +```html +
+ Black Friday prices are active! +
+``` + +## URL Parameters + +### ?profile= + +Applies a profile on page load (preserves existing cart). + +**Example:** +``` +https://example.com/checkout?profile=black_friday +``` + +### ?forceProfile= + +Applies a profile on page load (clears cart first). + +**Example:** +``` +https://example.com/checkout?forceProfile=vip +``` + +### ?packageProfile= + +Alternative parameter name for profile activation. + +**Example:** +``` +https://example.com/checkout?packageProfile=sale_20 +``` + +## Configuration + +Profiles are configured in `window.nextConfig`: + +```javascript +window.nextConfig = { + profiles: { + "profile_id": { + name: "Profile Name", + description: "Optional description", + packageMappings: { + 1: 101, // original_id: mapped_id + 2: 102, + // ... more mappings + } + } + }, + defaultProfile: "profile_id" // Optional default +}; +``` + +## CSS Classes + +The following CSS classes are automatically applied: + +- `.next-profile-switcher` - Applied to profile switcher elements +- `.next-profile-active` - Applied when profile is active +- `.next-profile-selector` - Applied to profile selector dropdowns +- `.next-loading` - Applied during profile switching + +## TypeScript Types + +```typescript +interface Profile { + id: string; + name: string; + description?: string; + packageMappings: Record; + reverseMapping?: Record; + isActive?: boolean; + priority?: number; +} + +interface ProfileState { + profiles: Map; + activeProfileId: string | null; + previousProfileId: string | null; + mappingHistory: MappingEvent[]; + originalCartSnapshot?: CartItem[]; +} + +interface MappingEvent { + timestamp: number; + profileId: string; + action: 'applied' | 'reverted' | 'switched'; + itemsAffected: number; + previousProfileId?: string; +} +``` \ No newline at end of file diff --git a/docs/campaign-cart/api-reference/url-parameters.md b/docs/campaign-cart/api-reference/url-parameters.md new file mode 100644 index 0000000..991643e --- /dev/null +++ b/docs/campaign-cart/api-reference/url-parameters.md @@ -0,0 +1,342 @@ +# URL Parameters API + +The SDK provides a comprehensive API for managing URL parameters programmatically. These methods allow you to control parameter-based visibility and behavior without relying solely on URL query strings. + +## Overview + +URL parameters are automatically captured from the page URL when the SDK initializes and stored in sessionStorage for the entire session. You can also programmatically set, get, and clear parameters using the JavaScript API. + +## Methods + +All parameter methods are available on the global `next` object after the SDK initializes. + +### setParam(key, value) + +Sets a single URL parameter. + +**Parameters:** +- `key` (string) - The parameter name +- `value` (string) - The parameter value + +**Example:** +```javascript +// Hide banner elements +next.setParam('banner', 'n'); + +// Enable debug mode +next.setParam('debug', 'true'); +``` + +### setParams(params) + +Sets multiple parameters at once, replacing all existing parameters. + +**Parameters:** +- `params` (object) - Key-value pairs of parameters + +**Example:** +```javascript +next.setParams({ + timer: 'n', + reviews: 'n', + banner: 'n', + exit: 'n' +}); +``` + +### mergeParams(params) + +Merges new parameters with existing ones, preserving parameters not included in the merge. + +**Parameters:** +- `params` (object) - Key-value pairs to merge + +**Example:** +```javascript +// Existing: { timer: 'n', banner: 'y' } +next.mergeParams({ + campaign: 'summer2024', + banner: 'n' +}); +// Result: { timer: 'n', banner: 'n', campaign: 'summer2024' } +``` + +### getParam(key) + +Gets the value of a specific parameter. + +**Parameters:** +- `key` (string) - The parameter name + +**Returns:** +- `string | null` - The parameter value or null if not set + +**Example:** +```javascript +const timerValue = next.getParam('timer'); +if (timerValue === 'n') { + console.log('Timer is disabled'); +} +``` + +### getAllParams() + +Gets all stored parameters as an object. + +**Returns:** +- `object` - All parameters as key-value pairs + +**Example:** +```javascript +const allParams = next.getAllParams(); +console.log('Current parameters:', allParams); +// Output: { timer: 'n', banner: 'n', debug: 'true' } +``` + +### hasParam(key) + +Checks if a parameter exists (regardless of its value). + +**Parameters:** +- `key` (string) - The parameter name + +**Returns:** +- `boolean` - True if the parameter exists + +**Example:** +```javascript +if (next.hasParam('debug')) { + console.log('Debug mode is active'); +} +``` + +### clearParam(key) + +Removes a specific parameter. + +**Parameters:** +- `key` (string) - The parameter name to remove + +**Example:** +```javascript +// Remove debug mode +next.clearParam('debug'); +``` + +### clearAllParams() + +Removes all stored parameters. + +**Example:** +```javascript +// Clear all parameters +next.clearAllParams(); +``` + +## Usage Examples + +### Feature Toggles + +Create a simple feature toggle system: + +```javascript +class FeatureToggle { + static enable(feature) { + next.setParam(`feature_${feature}`, 'enabled'); + } + + static disable(feature) { + next.setParam(`feature_${feature}`, 'disabled'); + } + + static isEnabled(feature) { + return next.getParam(`feature_${feature}`) === 'enabled'; + } + + static toggle(feature) { + if (this.isEnabled(feature)) { + this.disable(feature); + } else { + this.enable(feature); + } + } +} + +// Usage +FeatureToggle.enable('new_checkout'); +if (FeatureToggle.isEnabled('new_checkout')) { + // Show new checkout flow +} +``` + +### A/B Testing + +Implement A/B test variants: + +```javascript +function setABTestVariant(testName, variant) { + next.setParam(`ab_${testName}`, variant); +} + +function getABTestVariant(testName) { + return next.getParam(`ab_${testName}`) || 'control'; +} + +// Set variant +setABTestVariant('checkout_flow', 'variant_b'); + +// Use variant +const variant = getABTestVariant('checkout_flow'); +switch(variant) { + case 'variant_a': + // Show variant A + break; + case 'variant_b': + // Show variant B + break; + default: + // Show control +} +``` + +### User Preferences + +Store user preferences without cookies: + +```javascript +const UserPreferences = { + hideAnnoyances() { + next.setParams({ + timer: 'n', + exit: 'n', + loading: 'n', + banner: 'n', + reviews: 'n' + }); + }, + + showAll() { + next.clearAllParams(); + }, + + setTheme(theme) { + next.setParam('theme', theme); + }, + + getTheme() { + return next.getParam('theme') || 'light'; + } +}; + +// Usage +UserPreferences.hideAnnoyances(); +UserPreferences.setTheme('dark'); +``` + +### Debug Mode + +Toggle debug mode programmatically: + +```javascript +function toggleDebug() { + if (next.hasParam('debug')) { + next.clearParam('debug'); + console.log('Debug mode disabled'); + } else { + next.setParam('debug', 'true'); + console.log('Debug mode enabled'); + // Reload to apply debug mode + window.location.reload(); + } +} + +// Add keyboard shortcut (Ctrl+Shift+D) +document.addEventListener('keydown', (e) => { + if (e.ctrlKey && e.shiftKey && e.key === 'D') { + toggleDebug(); + } +}); +``` + +### Campaign Tracking + +Track marketing campaigns: + +```javascript +function trackCampaign(source, medium, campaign) { + next.mergeParams({ + utm_source: source, + utm_medium: medium, + utm_campaign: campaign, + campaign_timestamp: Date.now().toString() + }); +} + +function getCampaignInfo() { + return { + source: next.getParam('utm_source'), + medium: next.getParam('utm_medium'), + campaign: next.getParam('utm_campaign'), + timestamp: next.getParam('campaign_timestamp') + }; +} + +// Track a campaign +trackCampaign('email', 'newsletter', 'summer_sale_2024'); + +// Get campaign info +const campaign = getCampaignInfo(); +``` + +## Integration with Conditional Display + +Parameters set via the JavaScript API work seamlessly with the conditional display attributes: + +```html + +
+ Promotional banner +
+ + +
+ Debug information +
+``` + +## Storage and Persistence + +- Parameters are stored in sessionStorage with the key `next-url-params` +- They persist across page navigation within the same session +- New URL parameters override stored values with the same key +- Parameters are cleared when the browser session ends + +## Best Practices + +1. **Use consistent naming conventions**: Prefix related parameters (e.g., `feature_*`, `ab_*`, `utm_*`) + +2. **Document your parameters**: Keep a list of all parameters your application uses + +3. **Handle missing parameters gracefully**: Always provide defaults + ```javascript + const theme = next.getParam('theme') || 'light'; + ``` + +4. **Clean up unused parameters**: Remove parameters that are no longer needed + ```javascript + next.clearParam('temporary_flag'); + ``` + +5. **Use type-safe wrappers**: Create utility functions for commonly used parameters + ```javascript + const ParamUtils = { + isFeatureEnabled: (feature) => next.getParam(feature) === 'true', + setFeature: (feature, enabled) => next.setParam(feature, enabled ? 'true' : 'false') + }; + ``` + +## See Also + +- [URL Parameters (Attributes)](../attributes/url-parameters.md) - Using parameters with HTML attributes +- [Conditionals](../attributes/conditionals.md) - Show/hide elements based on conditions +- [Events](./events.md) - Parameter-related events \ No newline at end of file diff --git a/docs/campaign-cart/attributes/campaign.md b/docs/campaign-cart/attributes/campaign.md new file mode 100644 index 0000000..1d6ba76 --- /dev/null +++ b/docs/campaign-cart/attributes/campaign.md @@ -0,0 +1,69 @@ +# Campaign Attributes + +Display campaign-level information like name, currency, and language. + +## Available Attributes + +```html +{Campaign Name} +{Campaign Currency} +{Campaign Language} +``` + +## Examples + +### Basic Campaign Info + +```html +
+

Campaign Name

+

Currency: USD

+

Language: en

+
+``` + +### Currency Display + +```html + +$ + + +
+ $ + 99.99 +
+``` + +### Language-Based Content + +```html + +
+ Welcome to our store! +
+
+ ¡Bienvenido a nuestra tienda! +
+``` + +## Use Cases + +1. **Page Headers**: Display campaign name +2. **Currency Formatting**: Show appropriate currency +3. **Localization**: Conditional content by language +4. **Analytics**: Track campaign performance + +## Notes + +- Campaign data is loaded on SDK initialization +- Values are read-only +- Use for display purposes only + +## Additional Campaign Data + +TODO: Add information about: +- Campaign ID +- Campaign status +- Campaign dates +- Custom campaign fields \ No newline at end of file diff --git a/docs/campaign-cart/attributes/cart.md b/docs/campaign-cart/attributes/cart.md new file mode 100644 index 0000000..6fdfa01 --- /dev/null +++ b/docs/campaign-cart/attributes/cart.md @@ -0,0 +1,489 @@ +# Cart Attributes + +Display cart totals, quantities, and calculated values. Updates automatically when cart changes. + +## Cart Status Properties + +```html +Whether cart is empty +Whether cart has items +Number of different items +Total quantity of all items +Same as quantity (alias) +Display the discount code +Display the discount codes +Number of coupons applied +``` + +## Cart Financial Totals + +```html +Cart subtotal (formatted) +Cart subtotal with discounts applied (formatted) +Cart total (formatted) +Cart total excluding shipping (formatted) +Shipping cost (formatted) +Discount amount (formatted) +Total savings (formatted - exclude discounts) +Savings percentage (formatted - exclude discounts) +Total savings (formatted - include discounts) +Total savings percentage (formatted - include discounts) +Compare-at total (formatted) +``` + +### Including Discounts in Subtotal + +Use the `data-include-discounts` attribute to display the subtotal with applied discount codes: + +```html + +$100.00 + + +$90.00 + + +100 +90 +``` + +## Cart Conditionals + +```html +
Cart is empty
+
Cart has items
+
Specific item in cart
+
Cart total over $50
+
Cart has savings
+
Cart has coupon applied
+``` + +## Complete Cart Summary Example + +```html +
+

Cart Summary

+ + +
+

Your cart is empty

+ Continue Shopping +
+ + +
+
+

Items: 0

+

Quantity: 0

+
+ +
+
+ Subtotal: + $0.00 +
+ +
+ Shipping: + $0.00 +
+ +
+ Discount (-): + -$0.00 +
+ +
+ Total: + $0.00 +
+ +
+ Total (excl. shipping): + $0.00 +
+
+ +
+

+ You're saving $0 + (0%)! +

+
+
+
+``` + +## Free Shipping Threshold + +```html +
+
+

Add $0 more for free shipping!

+
+
+
+
+ +
+

✓ You qualify for free shipping!

+
+
+``` + +## Cart Items Renderer + +The `data-next-cart-items` attribute dynamically renders cart items using customizable templates. + +### Basic Usage + +```html + +
+ +
+``` + +### Template Configuration + +Templates can be specified in multiple ways (in priority order): + +#### 1. Template by ID +```html + + + + +
+
+``` + +#### 2. Template by CSS Selector +```html + + + + +
+
+``` + +#### 3. Inline Template String +```html +
+
+``` + +#### 4. Default: Inner HTML +```html +
+
+ {item.name} + {item.quantity} +
+
+``` + +### Available Template Variables + +#### Basic Item Properties +- `{item.id}` - Unique cart item identifier +- `{item.packageId}` - Package/product ID +- `{item.name}` - Product name +- `{item.title}` - Product title (alias for name) +- `{item.quantity}` - Quantity in cart +- `{item.image}` - Product image URL +- `{item.sku}` - Product SKU/external ID +- `{item.frequency}` - Purchase frequency (e.g., "One time", "Every month") + +#### Product & Variant Properties +- `{item.productId}` - Base product ID +- `{item.productName}` - Base product name (e.g., "Grounded Sheets") +- `{item.variantId}` - Product variant ID +- `{item.variantName}` - Full variant name (e.g., "Grounded Sheets - Obsidian Grey / Twin") +- `{item.variantSku}` - Variant-specific SKU (e.g., "BE-GRS-TWN-GR") +- `{item.variantAttributes}` - Raw array of variant attributes (for JSON output) +- `{item.variantAttributesFormatted}` - Formatted attributes as string (e.g., "Color: Obsidian Grey, Size: Twin") +- `{item.variantAttributesList}` - Formatted attributes as HTML spans + +#### Individual Variant Attributes +Access specific variant attributes directly: +- `{item.variantColor}` - Color value (e.g., "Obsidian Grey") +- `{item.variantSize}` - Size value (e.g., "Twin") +- `{item.variant.color}` - Alternative access for color +- `{item.variant.size}` - Alternative access for size +- `{item.variant_color}` - Another alternative format +- `{item.variant_size}` - Another alternative format + +Note: These individual accessors are dynamically generated based on the variant's attribute codes. + +#### Pricing Variables +- `{item.price}` - Total package price +- `{item.unitPrice}` - Individual unit price +- `{item.comparePrice}` - Original package price (before discount) +- `{item.unitComparePrice}` - Original unit price +- `{item.lineTotal}` - Line total (price × quantity) +- `{item.lineCompare}` - Original line total +- `{item.recurringPrice}` - Recurring price (if subscription) + +#### Savings Variables +- `{item.savingsAmount}` - Total savings amount (retail discount only) +- `{item.unitSavings}` - Per-unit savings +- `{item.savingsPct}` - Savings percentage (e.g., "50%") +- `{item.packageSavings}` - Package-level savings +- `{item.packageSavingsPct}` - Package savings percentage + +#### Coupon Discount Variables (New) +- `{item.discountAmount}` - Coupon discount amount for this item +- `{item.discountedPrice}` - Package price after coupon discount +- `{item.discountedLineTotal}` - Line total after coupon discount +- `{item.hasDiscount}` - Whether item has a coupon discount applied +- `{item.finalPrice}` - Final price to display (with discount if applicable) +- `{item.finalLineTotal}` - Final line total (with discount if applicable) + +#### Package Details +- `{item.packagePrice}` - Base package price +- `{item.packagePriceTotal}` - Package total price +- `{item.packageRetailPrice}` - Package retail price +- `{item.packageRetailTotal}` - Package retail total +- `{item.packageQty}` - Quantity in package + +#### Conditional Display Classes +- `{item.showCompare}` - "show" or "hide" based on savings +- `{item.showSavings}` - "show" or "hide" if has savings +- `{item.showRecurring}` - "show" or "hide" if recurring +- `{item.showUnitPrice}` - "show" or "hide" for multi-unit packages +- `{item.showDiscount}` - "show" or "hide" if has coupon discount +- `{item.showOriginalPrice}` - "show" or "hide" if price is discounted +- `{item.hasSavings}` - "true" or "false" +- `{item.isRecurring}` - "true" or "false" + +#### Raw Values (unformatted) +- `{item.price.raw}` - Numeric price value +- `{item.unitPrice.raw}` - Numeric unit price +- `{item.lineTotal.raw}` - Numeric line total +- `{item.savingsAmount.raw}` - Numeric savings +- `{item.savingsPct.raw}` - Numeric percentage +- `{item.discountAmount.raw}` - Numeric discount amount +- `{item.discountedPrice.raw}` - Numeric discounted price +- `{item.discountedLineTotal.raw}` - Numeric discounted line total +- `{item.finalPrice.raw}` - Numeric final price +- `{item.finalLineTotal.raw}` - Numeric final line total + +### Complete Example + +```html + + + + +
+
+``` + +### Working with Product Variants + +The cart now includes detailed variant information that can be displayed in templates: + +```html + +``` + +The variant attributes are now available in multiple formats: + +**All Attributes Together:** +- **`{item.variantAttributesFormatted}`** - Simple comma-separated string: "Color: Obsidian Grey, Size: Twin" +- **`{item.variantAttributesList}`** - HTML spans: `Color: Obsidian Grey Size: Twin` +- **`{item.variantAttributes}`** - Raw JSON array (for custom JavaScript processing) + +**Individual Attributes:** +```html + +
Color: {item.variantColor}
+ + +
Size: {item.variantSize}
+ + +
+ {item.variantColor} + {item.variantSize} +
+ + +
{item.variant.color} / {item.variant.size}
+
{item.variant_color} - {item.variant_size}
+``` + +### Additional Attributes + +- `data-empty-template` - Template to show when cart is empty +- `data-title-map` - JSON object to map package IDs to custom titles +- `data-group-items` - Group identical items together (combines quantities) + +```html + +
+
+ + +
+
+``` + +### Item Grouping + +When `data-group-items` is enabled, identical items (same packageId) are automatically grouped together: + +**Without Grouping:** +- Item 1: Grounded Sheets - Grey/Single (Qty: 1) +- Item 2: Grounded Sheets - Grey/Single (Qty: 1) +- Item 3: Grounded Sheets - Grey/Single (Qty: 1) + +**With Grouping:** +- Item: Grounded Sheets - Grey/Single (Qty: 3) + +This is useful when items are added individually but you want to display them as a single line with combined quantity. + +## Cart Item Details + +```html + +
+

Don't forget to add a warranty!

+
+ + +

Drones in cart: 0

+``` + +## Discount Display + +```html +
+

Applied discount: -

+

You saved: $0

+
+``` + +## Raw Values for Calculations + +```html + +
+

10% tip would be: $0

+
+ + +Raw subtotal value +Raw total value +Raw total excluding shipping +Raw shipping cost +Raw discount amount +Raw savings amount +Raw compare total +``` + +## Best Practices + +1. **Empty State**: Always handle empty cart scenario +2. **Real-time Updates**: Cart values update automatically +3. **Conditional Display**: Show relevant info based on cart state +4. **Savings Highlight**: Emphasize savings when present +5. **Threshold Messages**: Encourage higher order values \ No newline at end of file diff --git a/docs/campaign-cart/attributes/checkout.md b/docs/campaign-cart/attributes/checkout.md new file mode 100644 index 0000000..2a19689 --- /dev/null +++ b/docs/campaign-cart/attributes/checkout.md @@ -0,0 +1,278 @@ +# Checkout Review Enhancer + +The `CheckoutReviewEnhancer` automatically displays stored checkout data from previous steps, allowing users to review their information before completing their order. + +## Features + +- ✅ Automatically reads from `checkoutStore` +- ✅ Supports multiple format types (text, address, name, phone, currency) +- ✅ Auto-updates when checkout data changes +- ✅ Supports custom fallback text +- ✅ Works with any container element + +## Basic Usage + +### Simple Text Fields + +Display individual checkout fields: + +```html +
+
+ Contact: + +
+ +
+ Phone: + +
+
+``` + +### Full Address + +Display formatted address: + +```html +
+
+ Ship to: +
+
+
+``` + +### Full Name + +Display full name (combines fname + lname): + +```html +
+
+ Name: + +
+
+``` + +## Complete Example + +Here's a Shopify-style review section: + +```html +
+

Review your information

+ +
+ +
+
+
Contact
+
+
+
+ Change +
+
+ + +
+
+
Ship to
+
+
+
+ Change +
+
+ + +
+
+
Phone
+
+
+
+ Change +
+
+
+
+ + +``` + +## Attributes Reference + +| Attribute | Required | Description | Example | +|-----------|----------|-------------|---------| +| `data-next-enhancer` | Yes | Must be "checkout-review" on container | `data-next-enhancer="checkout-review"` | +| `data-next-checkout-review` | Yes | Field name to display | `data-next-checkout-review="email"` | +| `data-next-format` | No | Format type (text, address, name, phone, currency) | `data-next-format="address"` | +| `data-next-fallback` | No | Text to show if field is empty | `data-next-fallback="Not provided"` | + +## Available Fields + +All fields from `checkoutStore.formData`: + +- `email` - Customer email +- `fname` - First name +- `lname` - Last name +- `phone` - Phone number +- `address1` - Street address +- `address2` - Apartment, suite, etc. +- `city` - City +- `province` - State/Province +- `postal` - ZIP/Postal code +- `country` - Country code + +## Format Types + +### `text` (default) + +Displays the raw value as text: + +```html + + +``` + +### `address` + +Combines multiple address fields into a formatted address: + +```html +
+ +``` + +Includes: address1, address2 (if provided), city, province, postal, country + +### `name` + +Combines first and last name: + +```html + + +``` + +### `phone` + +Formats phone numbers: + +```html + + +``` + +### `currency` + +Formats numeric values as currency: + +```html + + +``` + +## Nested Fields + +You can access nested fields using dot notation: + +```html + +``` + +## Fallback Text + +Provide fallback text for empty fields: + +```html + +``` + +When the field is empty, it will display "No apartment number" and add the `next-review-empty` CSS class. + +## Auto-Updates + +The enhancer automatically subscribes to checkout store changes. If the user goes back and updates their information, the review section will update immediately when they return to the review page. + +## CSS Classes + +The enhancer adds the following CSS classes: + +- `next-review-empty` - Added to elements when the field value is empty or uses fallback text + +## JavaScript Access + +You can also manually register the enhancer: + +```javascript +const reviewContainer = document.querySelector('.checkout-review'); +const enhancer = await sdk.enhancers.register('checkout-review', reviewContainer); +``` + +## Notes + +- The enhancer must be placed inside a container with `data-next-enhancer="checkout-review"` +- All review fields within the container will be automatically populated +- The enhancer subscribes to checkout store updates for real-time changes +- Country codes are automatically converted to full country names diff --git a/docs/campaign-cart/attributes/conditionals.md b/docs/campaign-cart/attributes/conditionals.md new file mode 100644 index 0000000..6a73fd3 --- /dev/null +++ b/docs/campaign-cart/attributes/conditionals.md @@ -0,0 +1,160 @@ +# Conditional Display Attributes + +Show/hide elements based on conditions. Works with cart, package, selection, and order data. + +## Show/Hide Attributes + +```html +
Show when cart has items
+
Hide when cart is empty
+
Show when package has savings
+
Show when selector has selection
+``` + +## Comparison Conditions + +```html +
Show when cart total over $50
+
Show when 3+ items in cart
+
Show for single-unit packages
+
Hide when selection under $20
+``` + +## Advanced Conditionals + +```html +
Show when savings over 30%
+
Show for subscription products
+
Show for multi-pack selections
+
Hide when order has upsells
+``` + +## Boolean Properties + +### Cart Booleans +- `cart.isEmpty` - Cart has no items +- `cart.hasItems` - Cart has items +- `cart.hasSavings` - Cart has any savings +- `cart.hasCoupon` - Discount code applied + +### Package Booleans +- `package.hasSavings` - Package has savings +- `package.hasRetailPrice` - Has compare price +- `package.isBundle` - Multi-unit package +- `package.isRecurring` - Subscription product +- `package.isOneTime` - One-time purchase + +### Order Booleans +- `order.exists` - Order was found +- `order.isRecent` - Placed < 15 min ago +- `order.supportsUpsells` - Can add items +- `order.isTest` - Test order + +## Complex Examples + +### Tiered Messaging + +```html + +
+

+ Standard shipping: $9.99 +

+

+ Reduced shipping: $4.99 +

+

+ ✓ FREE SHIPPING! +

+
+``` + +### Progressive Disclosure + +```html + +
+ + + + +
+

VIP Options Available!

+ +
+
+``` + +### Conditional Product Info + +```html +
+ +
+ $99 +
+ + +
+ $79 + + $99 + + + Save 20% + +
+ + +
+

Auto-delivery every month

+
+
+``` + +### Smart Upsell Display + +```html + +
+ +
+

Protect Your Purchase

+ +
+ + +
+

Complete Your Setup

+ +
+
+``` + +## Function-Based Conditions + +```html + +
+ Package 5 is in your cart! +
+ + +
+ You qualify for free gift! +
+``` + +## Best Practices + +1. **Clear Logic**: Use simple, understandable conditions +2. **Fallbacks**: Always handle both states (show/hide) +3. **Performance**: Avoid complex calculations in conditions +4. **Testing**: Test all condition states +5. **Accessibility**: Don't hide critical information \ No newline at end of file diff --git a/docs/campaign-cart/attributes/formatting.md b/docs/campaign-cart/attributes/formatting.md new file mode 100644 index 0000000..49326f0 --- /dev/null +++ b/docs/campaign-cart/attributes/formatting.md @@ -0,0 +1,147 @@ +# Formatting Options + +Control how values are displayed with formatting attributes. + +## Format Types + +```html +Format as currency ($12.34) +Format as number (1,234.56) +``` + +## Conditional Hiding + +```html +Hide element if value is 0 +Hide element if value is false +Hide cents if .00 (shows $199 instead of $199.00) +``` + +## Mathematical Transformations + +```html +Multiply value by 2 +Divide value by 2 +``` + +## Complete Examples + +### Currency Formatting + +```html + +$99.99 + + +$99.99 + + +99.99 +``` + +### Conditional Display + +```html + +
+ Shipping: $0.00 +
+ + +
+ This is a subscription product +
+``` + +### Mathematical Operations + +```html + +$199.98 + + +$49.99 + + +
+ 10% deposit: $9.99 +
+``` + +## Raw vs Formatted Values + +```html + +$99.99 + + +99.99 + + +199.98 +``` + +## Advanced Formatting + +### Percentage Display + +```html + +30% + + +30 +``` + +### Conditional Formatting + +```html + +
+ + $99.99 + + + $199.99 + +
+``` + +### Custom Formatting with CSS + +```html + + + $99.99 + +``` + +## Best Practices + +1. **Use Raw for Math**: Always use `.raw` values for calculations +2. **Hide Zeros**: Use `data-hide-if-zero` for optional values +3. **Format Consistently**: Use same format throughout page +4. **Test Edge Cases**: Check formatting with various values +5. **Accessibility**: Ensure formatted values are readable + +## Common Patterns + +### Price Range Display +```html +$99 - +$297 +``` + +### Savings Calculator +```html +You save: $20 +(20%) +``` + +### Dynamic Pricing +```html +Price per unit: $33.33 +``` \ No newline at end of file diff --git a/docs/campaign-cart/attributes/index.md b/docs/campaign-cart/attributes/index.md new file mode 100644 index 0000000..e857cf6 --- /dev/null +++ b/docs/campaign-cart/attributes/index.md @@ -0,0 +1,34 @@ +--- +sidebar_label: Attributes +sidebar_position: 1 +--- + +# Data Attributes + +The Next Commerce JS SDK uses HTML data attributes to display dynamic content and control element visibility. + +## Overview + +- [Attributes Overview](/docs/campaign-cart/attributes/overview) - Introduction to attributes + +## Data Sources + +- [Campaign Attributes](/docs/campaign-cart/attributes/campaign) - Campaign-level data +- [Package Attributes](/docs/campaign-cart/attributes/package) - Product information +- [Selection Attributes](/docs/campaign-cart/attributes/selection) - Selected item data +- [Cart Attributes](/docs/campaign-cart/attributes/cart) - Shopping cart data +- [Order Attributes](/docs/campaign-cart/attributes/order) - Order details +- [Profile Attributes](/docs/campaign-cart/attributes/profile) - Profile-based data + +## Functionality + +- [Conditionals](/docs/campaign-cart/attributes/conditionals) - Show/hide logic +- [Formatting](/docs/campaign-cart/attributes/formatting) - Value formatting +- [Checkout Attributes](/docs/campaign-cart/attributes/checkout) - Checkout flow controls +- [URL Parameters](/docs/campaign-cart/attributes/url-parameters) - URL-based configuration + +## Learn More + +- [API Reference - Data Attributes](/docs/campaign-cart/api-reference/data-attributes) - Complete attribute reference +- [Cart System](/docs/campaign-cart/cart-system/overview) - Using attributes with cart management + diff --git a/docs/campaign-cart/attributes/order.md b/docs/campaign-cart/attributes/order.md new file mode 100644 index 0000000..8683128 --- /dev/null +++ b/docs/campaign-cart/attributes/order.md @@ -0,0 +1,645 @@ +# Order Attributes + +Display order confirmation data. Requires order ID in URL (?ref_id=ORDER_ID) or manual loading. + +## Basic Order Properties + +```html + +Order ID +Order number +Reference ID (snake_case) +Reference ID (camelCase) + + +Order status +Is test order (boolean) +Is test order (alias) +Shows "🧪 TEST ORDER" if test + + +View Order Status +View Order Status (alias) + + +Creation date +Creation date (alias) +Currency code +Supports upsells +Supports upsells (alias) +``` + +## Order Financial Data + +```html + +Grand total including tax and shipping +Grand total (alias for total_incl_tax) +Line items only (excludes shipping and tax) +Line items only (alias for subtotal) +Total excl tax but INCLUDES shipping + + +Tax amount (snake_case) +Tax amount (alias) +Shipping including tax (snake_case) +Shipping including tax (alias) +Shipping excluding tax (snake_case) +Shipping excluding tax (alias) +Shipping tax amount (snake_case) +Shipping tax amount (alias) + + +Total discounts (snake_case) +Total discounts (alias) +Calculated savings amount +Savings percentage + + +Payment method +Payment method (alias) +Shipping method +Shipping method (alias) + + +Total as number +Subtotal as number +Tax as number +Shipping as number +Discounts as number +``` + +## Customer Information + +```html + +Full customer name +First name only +Last name only +Customer email +Customer phone + + +Accepts marketing (boolean) +Customer language +Customer IP address + + +Full name +Email +First name +Last name +Phone +``` + +## Address Information + +```html + +Full formatted address +Recipient name +Address line 1 +Address line 2 +City +State/Province +ZIP code +Postcode (alias) +Country +Phone number + + +Full formatted address +Recipient name +Address line 1 +Address line 2 +City +State/Province +ZIP code +Postcode (alias) +Country +Phone number +``` + +## Line Items / Products + +```html + +Number of line items +Total quantity of all items +Number of upsell items +Main product title (first item) +Main product SKU (first item) + + +Number of line items +Total quantity + + +First item title +First item title (snake_case) +First item SKU +First item SKU (snake_case) +First item quantity +First item image URL + + +Unit price incl tax (formatted) +Unit price excl tax (formatted) +Original price before discounts +Original price incl tax before discounts +Line total incl tax (formatted) +Line total excl tax (formatted) + + +Unit price as number +Unit price excl tax as number +Original price as number +Original price incl tax as number +Line total as number +Line total excl tax as number + + +Is upsell item (boolean) + + +Second item title +Third item title +``` + +## Attribution / UTM Tracking + +```html + +UTM source +UTM source (alias) +UTM medium +UTM medium (alias) +UTM campaign +UTM campaign (alias) +UTM term +UTM term (alias) +UTM content +UTM content (alias) + + +Google Click ID +Funnel name +Affiliate code +Has any tracking (boolean) +``` + +## Order Boolean Properties + +```html + +
Order found
+
Order is loading
+
Unable to load order
+
Test order
+ + +
Order has items
+
Order is empty
+
Shipping charged
+
Tax charged
+
Order has discounts
+
Order has savings
+
Order has upsell items
+ + +
Order accepts additional items
+
Order accepts additional items (snake_case)
+ + +
Order placed less than 15 minutes ago
+
Order placed more than 15 minutes ago
+
New order (placed < 15 min ago)
+``` + +## Loading States + +```html + +
+ Loading order... +
+ + +
+

Error: Unable to load order

+
+ + +
+ +
+``` + +## Special Features + +### Auto-Loading from URL + +The order will automatically be loaded if the URL contains `?ref_id=XXX`: +``` +https://example.com/confirmation?ref_id=ORDER_123 +``` + +### CSS Classes + +The enhancer automatically adds classes to elements: +- `next-loading` - Added when order is loading +- `next-loaded` - Added when order is successfully loaded +- `next-error` - Added when there's an error loading the order + +### Link Handling + +When using order status URL on `` tags, the href is automatically set: +```html + +View Order Status +``` + +## Important: Order Total Breakdown + +### Understanding Order Totals + +When displaying order totals, it's important to understand what each attribute includes: + +- **`order.subtotal`** - Line items only (excludes shipping and tax) + - Example: $53.99 (just the products) + +- **`order.total_excl_tax`** - Total excluding tax but INCLUDING shipping + - Example: $63.98 (products $53.99 + shipping $9.99) + +- **`order.total`** or **`order.total_incl_tax`** - Grand total (includes everything) + - Example: $69.10 (products + shipping + all taxes) + +### Shopify-like Order Summary Example + +```html + +
Subtotal: $0.00
+ + +
Shipping: $0.00
+ + +
Tax: $0.00
+ + +
+ Discount: -$0.00 +
+ + +
Total: $0.00
+``` + +## Complete Order Confirmation Example + +```html +
+ +
+

Loading your order...

+
+ + +
+

Order Not Found

+

Error: Unknown error

+

Please check your email for the order confirmation.

+
+ + +
+

Thank You for Your Order!

+ + +
+

🧪 TEST ORDER

+
+ +
+

Order #-

+

Reference: -

+

Placed on -

+

Status: Processing

+
+ +
+

Customer Information

+

-

+

-

+

+ Phone: - +

+

+ ✓ Subscribed to marketing emails +

+
+ +
+
+

Shipping Address

+
+ -
+ -
+ + -
+
+ -, + - + -
+ - +
+

Method: -

+
+ +
+

Billing Address

+
-
+
+
+ +
+

Order Items (0)

+

Total Quantity: 0

+ + +
+

-

+

SKU: -

+
+ + +

+ Includes 0 upsell item(s) +

+
+ +
+

Order Summary

+
+
+ Subtotal: + $0.00 +
+ +
+ Shipping: + $0.00 +
+ +
+ Tax: + $0.00 +
+ +
+ Discount: + -$0.00 +
+ +
+ You saved: + + $0.00 + (0%) + +
+ +
+ Total: + $0.00 +
+
+ +

Payment Method: -

+
+ + +
+

Campaign Information

+

+ Source: - +

+

+ Campaign: - +

+

+ Affiliate: - +

+
+ + + + + +
+

Don't Miss Out!

+

Add these items to your order while you can:

+ View Special Offers +
+
+
+``` + +## Order Items Renderer + +The `data-next-order-items` attribute dynamically renders order line items using customizable templates. + +### Basic Usage + +```html + +
+ +
+``` + +### Template Configuration + +Templates can be specified in multiple ways (in priority order): + +#### 1. Template by ID +```html + + + + +
+
+``` + +#### 2. Template by CSS Selector +```html + + + + +
+
+``` + +#### 3. Inline Template String +```html +
+
+``` + +#### 4. Default: Inner HTML +```html +
+
+ {item.name} + {item.quantity} + {item.lineTotal} +
+
+``` + +### Available Template Variables + +#### Basic Item Properties +- `{item.id}` - Order line ID +- `{item.name}` - Product name +- `{item.title}` - Product title (alias for name) +- `{item.sku}` - Product SKU +- `{item.quantity}` - Quantity ordered +- `{item.description}` - Product description +- `{item.variant}` - Variant title (e.g., "Size: Large") +- `{item.image}` - Product image URL + +#### Pricing Variables (formatted) +- `{item.price}` - Unit price including tax +- `{item.priceExclTax}` - Unit price excluding tax +- `{item.unitTax}` - Tax per unit +- `{item.lineTotal}` - Line total including tax +- `{item.lineTotalExclTax}` - Line total excluding tax +- `{item.lineTax}` - Total tax for line + +#### Original Pricing Before Discounts (formatted) +- `{item.priceExclDiscounts}` - Original unit price before discounts (including tax) +- `{item.priceExclTaxExclDiscounts}` - Original unit price before discounts and tax +- `{item.lineTotalExclDiscounts}` - Original line total before discounts (including tax) +- `{item.lineTotalExclTaxExclDiscounts}` - Original line total before discounts and tax +- `{item.unitDiscount}` - Discount amount per unit +- `{item.lineDiscount}` - Total discount amount for the line + +#### Status Flags +- `{item.isUpsell}` - "true" or "false" if item is an upsell +- `{item.upsellBadge}` - "UPSELL" text or empty +- `{item.hasImage}` - "true" or "false" +- `{item.hasDescription}` - "true" or "false" +- `{item.hasVariant}` - "true" or "false" +- `{item.hasTax}` - "true" or "false" +- `{item.hasDiscount}` - "true" or "false" if item has a discount + +#### Conditional Display Classes +- `{item.showUpsell}` - "show" or "hide" for upsell badge +- `{item.showImage}` - "show" or "hide" based on image availability +- `{item.showDescription}` - "show" or "hide" if has description +- `{item.showVariant}` - "show" or "hide" if has variant +- `{item.showTax}` - "show" or "hide" if has tax +- `{item.showDiscount}` - "show" or "hide" if item has a discount + +### Complete Example + +```html + + + + +
+
+``` + +### Additional Attributes + +- `data-empty-template` - Template to show when order has no items + +```html +
+
+``` + +## Test Order Banner + +```html +
+

⚠️ This is a test order

+ +
+``` + +## Best Practices + +1. **Error Handling**: Always include loading and error states +2. **Test Orders**: Clearly indicate test orders with the testBadge +3. **Upsell Window**: Show upsells only when `order.supportsUpsells` is true +4. **Complete Info**: Display all relevant order details for customer reference +5. **Loading State**: Handle the three states: loading, error, and success +6. **Raw Values**: Use `.raw` suffix when you need numeric values for calculations +7. **Attribution**: Show campaign data when available for marketing insights +8. **Line Items**: Use the order items renderer for complex item displays \ No newline at end of file diff --git a/docs/campaign-cart/attributes/overview.md b/docs/campaign-cart/attributes/overview.md new file mode 100644 index 0000000..17330bc --- /dev/null +++ b/docs/campaign-cart/attributes/overview.md @@ -0,0 +1,74 @@ +# Attributes Overview + +The Next Commerce JS SDK uses HTML data attributes to display dynamic content and control element visibility. + +## Types of Attributes + +### Display Attributes +Show dynamic data that updates automatically: +```html +$0.00 +``` + +### Conditional Attributes +Show/hide elements based on conditions: +```html +
Cart has items!
+``` + +### Action Attributes +Trigger actions like adding to cart: +```html + +``` + +## Data Sources + +Attributes can display data from: +- **Campaign** - Campaign-level information +- **Package** - Product/package details +- **Selection** - Currently selected items +- **Cart** - Shopping cart data +- **Order** - Order confirmation details + +## Basic Examples + +```html + +$0.00 + + +
Your cart is empty
+ + +
+ Save 0% +
+``` + +## Context Awareness + +Attributes work within context: + +```html + +
+ Product Name + $0.00 +
+ + +Product 5 Name +``` + +## Learn More + +- [Campaign Attributes](campaign.md) - Campaign-level data +- [Package Attributes](package.md) - Product information +- [Selection Attributes](selection.md) - Selected item data +- [Cart Attributes](cart.md) - Shopping cart data +- [Order Attributes](order.md) - Order details +- [Conditionals](conditionals.md) - Show/hide logic +- [Formatting](formatting.md) - Value formatting \ No newline at end of file diff --git a/docs/campaign-cart/attributes/package.md b/docs/campaign-cart/attributes/package.md new file mode 100644 index 0000000..a30dcc9 --- /dev/null +++ b/docs/campaign-cart/attributes/package.md @@ -0,0 +1,190 @@ +# Package Attributes + +Display package/product data with context awareness. These work within package context or with explicit package ID. + +## Basic Package Properties + +### Identifiers & Info +```html +Package reference ID +External package ID +Package name +Package image URL +``` + +### Quantity & Units +```html +Number of units in package +Number of units (alias for qty) +``` + +### Subscription Info +```html +If package is recurring +Recurring interval frequency +Recurring interval count +``` + +## Pricing Properties + +### Base Prices +```html +Per-unit price (formatted) +Total package price (formatted) +Same as package.price (alias) +Same as package.price_total (alias) +``` + +### Retail/Compare Prices +```html +Per-unit retail price (formatted) +Total retail price (formatted) +Calculated unit retail price (formatted) +Compare price (alias for price_retail_total) +Compare total (alias for price_retail_total) +``` + +### Recurring Pricing +```html +Recurring subscription price (formatted) +Total recurring price (formatted) +``` + +### Discount-Adjusted Prices +```html +Unit price after cart discounts +Total price after cart discounts +Final unit price after all discounts +Final total price after all discounts +``` + +## Savings & Discounts + +### Package Savings (vs Retail Price) +```html +Savings vs retail (formatted) +Savings percentage vs retail (formatted) +Per-unit savings vs retail +Per-unit savings percentage +``` + +### Cart Discounts Applied +```html +Discount amount from cart codes +Total savings (retail + discounts) +Total savings percentage +Combined savings amount +Combined savings percentage +``` + +## Boolean Properties (for conditionals) + +```html +
This package has savings vs retail
+
This package has retail price
+
This package has cart discount applied
+
This package has any savings (retail or discount)
+
This package is a bundle
+
This package is a subscription
+
This package is a one time order
+``` + +## Raw Values (unformatted) + +### Price Values +```html +Raw price value +Raw total price +Raw retail price +Raw retail total +Raw unit price +Raw final price +Raw final total +``` + +### Savings Values +```html +Raw savings amount +Raw savings percentage +Raw discount amount +Raw total savings +Raw total savings percentage +``` + +## Context Usage + +### Within Package Context + +```html +
+

Product Name

+ +

$0.00

+

+ Save 0% +

+
+``` + +### Explicit Package Reference + +```html + +Package 5 Name +Package 5 Price +``` + +## Complete Product Card Example + +```html +
+ + +

Product Name

+ +
+ + $0.00 + + + + $0.00 + +
+ + +
+ + Save 0% + ($0.00) + +
+ + +
+ + Discount Applied: $0.00 + +
+ +
+ 1 pack bundle +
+ +
+ Delivered every month +
+ + +
+``` + +## Best Practices + +1. **Use Context**: Wrap elements in package container for cleaner code +2. **Show Savings**: Always display savings when available +3. **Bundle Info**: Show quantity for multi-packs +4. **Subscription Details**: Display frequency for recurring items +5. **Fallback Values**: Include default text in spans \ No newline at end of file diff --git a/docs/campaign-cart/attributes/profile.md b/docs/campaign-cart/attributes/profile.md new file mode 100644 index 0000000..03398a9 --- /dev/null +++ b/docs/campaign-cart/attributes/profile.md @@ -0,0 +1,273 @@ +# Profile Attributes + +Complete reference for profile-related HTML attributes and display properties. + +## Profile Display Properties + +Display current profile information and state. + +### Basic Profile Properties +```html + +exit_10 +exit_10 + + +Exit 10% Discount + + +true +``` + +## Profile Conditional Display + +Show or hide elements based on active profile. + +### Simple Profile Conditionals +```html + +
+ Black Friday prices are active! Save up to 50%! +
+ + +
+ Special promotional content +
+ + +
+ Welcome VIP member! Enjoy exclusive pricing. +
+ +
+ Don't leave! Here's your 10% discount. +
+``` + +### Complex Profile Conditionals +```html + +
+ Black Friday Sale Active +
+ + +
+ A special profile is currently active +
+ + +
+ VIP pricing applied +
+ + +
+ Seasonal sale profile available +
+``` + +## Profile Switcher Attributes + +Create interactive profile switching elements. + +### Profile Switch Button +```html + + + + + + + +
+ + + +
+``` + +### Profile Selector Dropdown +```html + + + + + +``` + +## Profile Switcher Options + +### data-next-clear-cart +Clears the cart before applying the new profile. +- **Values**: `true` | `false` +- **Default**: `false` + +```html + +``` + +### data-next-preserve-quantities +Maintains item quantities when swapping packages. +- **Values**: `true` | `false` +- **Default**: `true` + +```html + +``` + +### data-next-active-text / data-next-inactive-text +Dynamic button text based on profile state. + +```html + +``` + +## CSS Classes + +Automatically applied classes for styling profile elements. + +### Profile State Classes +- `.next-profile-active` - Applied when the profile is active +- `.next-profile-switcher` - Applied to profile switcher buttons +- `.next-profile-selector` - Applied to profile selector dropdowns +- `.next-profile-loading` - Applied during profile switching + +### Conditional Display Classes +- `.next-condition-met` - Applied when profile condition is true +- `.next-condition-not-met` - Applied when profile condition is false +- `.next-visible` - Applied when element should be shown +- `.next-hidden` - Applied when element should be hidden + +## Real-World Examples + +### Exit Intent Profile +```html + +
+

Wait! Don't Leave Yet!

+

We've activated a special 10% discount just for you!

+ + +
+``` + +### VIP Member Dashboard +```html + +
+

Welcome VIP Member!

+
+

Your Benefits:

+
    +
  • Exclusive VIP pricing
  • +
  • Free shipping on all orders
  • +
  • Priority customer support
  • +
+
+ +
+``` + +### Dynamic Pricing Display +```html + +
+ +
+

Regular Price

+ $99.99 +
+ + +
+

BLACK FRIDAY DEAL!

+ $99.99 + $49.99 + Save 50%! +
+
+``` + +### Profile-Based Navigation +```html + + +``` + +## JavaScript Events + +Listen for profile-related events: + +```javascript +// Profile applied +window.next.on('profile:applied', (data) => { + console.log(`Profile ${data.profileId} applied`); + console.log(`${data.itemsSwapped} items updated`); +}); + +// Profile reverted +window.next.on('profile:reverted', (data) => { + console.log(`Reverted from ${data.previousProfileId}`); +}); + +// Profile switched +window.next.on('profile:switched', (data) => { + console.log(`Switched from ${data.fromProfileId} to ${data.toProfileId}`); +}); +``` + +## Notes + +1. **Profile Priority**: Only one profile can be active at a time +2. **Package Mapping**: Profiles map package IDs to different packages (e.g., regular → sale version) +3. **Cart Persistence**: By default, cart items are swapped to mapped packages when profiles change +4. **URL Parameters**: Profiles can be activated via URL: `?profile=black_friday` or `?forceProfile=vip` +5. **Session Storage**: Active profile persists across page refreshes in the same session \ No newline at end of file diff --git a/docs/campaign-cart/attributes/selection.md b/docs/campaign-cart/attributes/selection.md new file mode 100644 index 0000000..e7dca98 --- /dev/null +++ b/docs/campaign-cart/attributes/selection.md @@ -0,0 +1,230 @@ +# Selection Attributes + +Display data based on currently selected package in a selector. Updates when selection changes. + +## Basic Selection Properties + +```html +Selected package name +Selected package ID +Selected quantity +Whether something is selected +``` + +## Selection Pricing + +```html +Unit price of selection +Total price of selection +Compare/retail total +Price per unit +``` + +## Selection Calculated Fields + +```html +Total savings +Savings percentage +Has savings boolean +Is multi-pack +Total units selected +``` + +## Mathematical Expressions + +```html +10% of total +Price plus $5 +Total minus $10 +Half of total +``` + +## Complete Example + +```html + +
+
+ 1 Pack - $99 +
+
+ 3 Pack - $249 +
+
+ 5 Pack - $399 +
+
+ + +
+

Your Selection

+ + +

+ Please select a package +

+ + +
+

Selected: -

+

Quantity: 0 units

+

Price: $0

+ +
+

You save: $0 + (0%) +

+
+
+ + + +
+``` + +## Dynamic Pricing Display + +```html + +
+ +
+ +
+

+ Basic protection for under $20 +

+

+ Premium protection +

+

+ Ultimate protection package +

+
+``` + +## Multiple Selectors + +```html + +
+ +
+ + +
+ +
+ + +
+

Product: $0

+

Accessory: $0

+
+

Total: $0

+
+``` + +## Quantity Controls + +Add quantity increase/decrease controls to selector cards to allow users to adjust quantities. + +### Basic Quantity Controls + +```html +
+ + +

Product Name

+

$19.99

+ + +
+ + 1 + +
+ + +

+ Total: $19.99 +

+
+``` + +### Quantity Control Attributes + +- `data-next-quantity-increase` - Button to increase quantity +- `data-next-quantity-decrease` - Button to decrease quantity (disabled at minimum) +- `data-next-quantity-display` - Element to display current quantity +- `data-next-min-quantity` - Minimum allowed quantity (default: 1) +- `data-next-max-quantity` - Maximum allowed quantity (default: 999) + +### Complete Example with Quantity Controls + +```html +
+ +
+ +
+ +

Premium Package

+ + +

+ $19.99 each +

+ + +
+ + 1 + +
+ + +

+ Total: $19.99 +

+ + +

+ You Save: $0 +

+
+
+
+``` + +### Behavior Notes + +1. **Auto-disable**: Decrease button automatically gets `disabled` attribute and `next-disabled` class when at minimum quantity +2. **Auto-disable**: Increase button automatically gets `disabled` attribute and `next-disabled` class when at maximum quantity +3. **Cart Sync**: In `swap` mode, quantity changes automatically update the cart +4. **Display Updates**: All `selection.{selectorId}.total` elements update when quantity changes +5. **Event Prevention**: Quantity button clicks don't trigger card selection + +## Best Practices + +1. **Unique IDs**: Use descriptive selector IDs +2. **Show Feedback**: Display selection details +3. **Conditional UI**: Hide/show based on selection +4. **Price Updates**: Show real-time pricing +5. **Validation**: Ensure selection before actions +6. **Quantity Limits**: Set appropriate min/max quantities for your use case \ No newline at end of file diff --git a/docs/campaign-cart/attributes/url-parameters.md b/docs/campaign-cart/attributes/url-parameters.md new file mode 100644 index 0000000..ebc9706 --- /dev/null +++ b/docs/campaign-cart/attributes/url-parameters.md @@ -0,0 +1,314 @@ +# URL Parameters + +URL parameters provide a powerful way to control element visibility and behavior based on query string values. Parameters are captured when the SDK initializes and persist throughout the user's session. + +## Overview + +The URL parameter system allows you to: +- Control element visibility based on URL parameters +- Store parameters across the entire session (even when navigating between pages) +- Override stored parameters with new values from subsequent page loads +- Use parameters in conditional display logic + +## How It Works + +1. **Parameter Capture**: When the SDK initializes, all URL parameters are automatically captured and stored in sessionStorage +2. **Session Persistence**: Parameters remain available throughout the user's session, even when navigating to pages without those parameters +3. **Parameter Override**: If a user visits a new page with different parameter values, the new values override the stored ones +4. **Conditional Display**: Use `data-next-show` and `data-next-hide` attributes to show/hide elements based on parameter values + +## Basic Usage + +### Hiding Elements Based on Parameters + +```html + +
+ As seen on TV section +
+ + +
+
00:00:00
+
+ + +
+ Customer testimonials... +
+``` + +### Showing Elements Based on Parameters + +```html + +
+ Debug mode is enabled +
+ + +
+ Simple header without promotional banner +
+``` + +## Supported Conditions + +### Equality Checks + +```html + +
+ Advanced options +
+ + +
+ Dark theme styles +
+``` + +### Existence Checks + +```html + +
+ Preview mode active +
+ + +
+ Login required message +
+``` + +### Numeric Comparisons + +```html + +
+ Bulk discount available! +
+ +
+ Age-restricted content +
+``` + +## Common Use Cases + +### 1. Feature Flags + +Control which features are visible to users: + +```html + +
+
+ Special offer before you leave! +
+
+ + +
+
Loading...
+
+``` + +### 2. A/B Testing + +Show different variations based on parameters: + +```html + +
+ +
+ + +
+ +
+``` + +### 3. Development/Testing + +Show debug information or test elements: + +```html + +
+
Cart State: 
+
+ + +
+ TEST MODE - Orders will not be processed +
+``` + +### 4. Marketing Campaigns + +Customize content based on campaign parameters: + +```html + +
+ Summer Sale - 50% Off Everything! +
+ + +
+ Regular pricing section +
+
+ VIP exclusive pricing! +
+``` + +## Advanced Features + +### Combining with Other Conditions + +URL parameters can be combined with other conditional logic: + +```html + +
+ Special upsell offer for existing customers! +
+ + +
+ Gold + Premium member exclusive content +
+``` + +### Parameter Priority + +When the same parameter appears multiple times: +1. URL parameters on the current page have highest priority +2. Previously stored session parameters are used as fallback +3. New parameter values override old ones + +Example flow: +``` +Page 1: ?banner=n&theme=dark + → Stores: banner=n, theme=dark + +Page 2: ?theme=light + → Updates: theme=light + → Keeps: banner=n + +Page 3: (no parameters) + → Uses stored: banner=n, theme=light +``` + +## JavaScript API + +URL parameters can also be managed programmatically using the SDK's JavaScript API. This allows you to set, get, and clear parameters without modifying the URL. + +For complete API documentation, see [URL Parameters API Reference](../api-reference/url-parameters.md). + +### Quick Examples + +```javascript +// Set parameters programmatically +next.setParam('banner', 'n'); +next.setParams({ timer: 'n', reviews: 'n' }); + +// Check and get parameters +if (next.hasParam('debug')) { + const debugValue = next.getParam('debug'); +} + +// Clear parameters +next.clearParam('timer'); +``` + +## Debugging + +### View Current Parameters + +To debug stored parameters, open the browser console and run: + +```javascript +// View all stored parameters (debug method) +nextDebug.stores.parameter().debug() + +// Or using the SDK +const params = next.getAllParams(); +console.log('Current parameters:', params); + +// Check specific parameter +nextDebug.stores.parameter().getParam('seen') + +// Check if parameter exists +nextDebug.stores.parameter().hasParam('timer') +``` + +### Common Issues + +1. **Parameters not persisting**: Check that sessionStorage is not being cleared +2. **Wrong values**: Remember that URL parameters override stored values +3. **Case sensitivity**: Parameter names are case-sensitive (`param.Test` ≠ `param.test`) +4. **Webflow removes quotes**: Webflow may strip quotes from attribute values. Both formats work: + - With quotes: `data-next-hide="param.timer == 'n'"` + - Without quotes: `data-next-hide="param.timer==n"` (Webflow format) + - The SDK automatically handles unquoted values for param comparisons + +## Best Practices + +1. **Use descriptive parameter names**: `hideReviews` is better than `hr` +2. **Be consistent with values**: Use 'true'/'false' or 'y'/'n' consistently +3. **Document your parameters**: Keep a list of all parameters your site uses +4. **Consider security**: Don't use parameters for sensitive features without proper validation +5. **Test thoroughly**: Test parameter behavior across page navigation + +## Implementation Reference + +The URL parameter system consists of three main components: + +1. **Parameter Store** (`src/stores/parameterStore.ts`): Manages parameter storage and retrieval +2. **SDK Initializer** (`src/enhancers/core/SDKInitializer.ts`): Captures parameters on page load +3. **Conditional Display Enhancer** (`src/enhancers/display/ConditionalDisplayEnhancer.ts`): Evaluates parameter conditions + +## Examples from parameters.md + +Based on your requirements, here are the implemented examples: + +```html + +
+
Coupon popup content
+
+ + +
+
00:00:00
+
+ + +
+ Customer reviews section +
+ + +
+
Loading...
+
+ + +
+ Promotional banner content +
+ + +
+ As seen on TV/Media logos +
+``` + +## Summary + +URL parameters provide a flexible, session-persistent way to control element visibility and customize user experience without modifying code. They're perfect for testing, feature flags, marketing campaigns, and creating dynamic, personalized experiences. \ No newline at end of file diff --git a/docs/campaign-cart/attribution/index.md b/docs/campaign-cart/attribution/index.md new file mode 100644 index 0000000..1cfb831 --- /dev/null +++ b/docs/campaign-cart/attribution/index.md @@ -0,0 +1,16 @@ +--- +sidebar_label: Attribution +sidebar_position: 1 +--- + +# Attribution + +Attribution tracking and URL parameters. + +- [URL Parameters](/docs/campaign-cart/attribution/url-parameters) - Attribution URL parameters + +## Related + +- [API Reference - Attribution](/docs/campaign-cart/api-reference/attribution) - Attribution API +- [Getting Started - URL Parameters](/docs/campaign-cart/getting-started/url-parameters) - Basic URL parameters + diff --git a/docs/campaign-cart/attribution/url-parameters.md b/docs/campaign-cart/attribution/url-parameters.md new file mode 100644 index 0000000..3103b24 --- /dev/null +++ b/docs/campaign-cart/attribution/url-parameters.md @@ -0,0 +1,521 @@ +# Attribution URL Parameters + +The Next Commerce JS SDK automatically captures attribution data from URL parameters to track marketing campaigns, affiliate referrals, and conversion sources. This data is persisted throughout the user's session and included with all orders. + +## Overview + +Attribution parameters are: +- **Automatically captured** from URL query strings when the SDK initializes +- **Persisted** in sessionStorage throughout the user's session +- **URL parameters always override** any existing persisted values +- **Included automatically** in checkout and order API calls +- **Cross-page persistent** - navigate anywhere and attribution is preserved + +## Core Attribution Parameters + +### Funnel + +Identifies the marketing funnel or campaign flow. + +**Parameter:** `funnel` + +**Example:** +``` +https://yoursite.com/checkout?funnel=summer-sale-2024 +``` + +**Usage:** +- Track different marketing funnels +- Identify which landing page or campaign the user came from +- Segment orders by funnel for reporting + +**Behavior:** +- Can also be set via meta tags (URL parameter takes priority) +- Once set, persists across all pages in the session +- URL parameter will override any existing funnel value +- Also accepts `next.setAttribution({ funnel: 'funnel-name' })` programmatically + +### Affiliate ID + +Tracks affiliate or partner referrals. + +**Parameters:** `affid` or `aff` (both accepted) + +**Example:** +``` +https://yoursite.com/product?affid=partner123 +https://yoursite.com/product?aff=affiliate-xyz +``` + +**Usage:** +- Track affiliate commissions +- Attribute sales to specific partners +- Segment traffic by referral source + +### Google Click ID (GCLID) + +Google Ads click identifier for conversion tracking. + +**Parameter:** `gclid` + +**Example:** +``` +https://yoursite.com/landing?gclid=EAIaIQobChMI7... +``` + +**Usage:** +- Google Ads conversion tracking +- Automatically captured from Google Ads campaigns +- Required for Google Ads conversion API + +## UTM Parameters + +Standard UTM tracking parameters for campaign analytics. + +### UTM Source + +Identifies the source of traffic (e.g., google, facebook, newsletter). + +**Parameter:** `utm_source` + +**Example:** +``` +https://yoursite.com/offer?utm_source=facebook +``` + +### UTM Medium + +Identifies the marketing medium (e.g., cpc, email, social). + +**Parameter:** `utm_medium` + +**Example:** +``` +https://yoursite.com/offer?utm_medium=cpc +``` + +### UTM Campaign + +Identifies the specific campaign. + +**Parameter:** `utm_campaign` + +**Example:** +``` +https://yoursite.com/offer?utm_campaign=summer-sale-2024 +``` + +### UTM Content + +Identifies specific ad or link content (for A/B testing). + +**Parameter:** `utm_content` + +**Example:** +``` +https://yoursite.com/offer?utm_content=banner-ad-blue +``` + +### UTM Term + +Identifies paid search keywords. + +**Parameter:** `utm_term` + +**Example:** +``` +https://yoursite.com/offer?utm_term=weight+loss+supplement +``` + +### Complete UTM Example + +``` +https://yoursite.com/product?utm_source=facebook&utm_medium=cpc&utm_campaign=summer-2024&utm_content=carousel-ad-1&utm_term=fitness +``` + +## Subaffiliate Tracking + +Track up to 5 levels of sub-affiliate data for multi-tier affiliate programs. + +**Parameters:** `subaffiliate1-5` or `sub1-5` (both formats accepted) + +**Character Limit:** Each subaffiliate value is limited to 225 characters (automatically truncated with a warning) + +**Examples:** +``` +https://yoursite.com/product?subaffiliate1=region-west&subaffiliate2=agent-123 +https://yoursite.com/product?sub1=tier1&sub2=tier2&sub3=tier3 +``` + +**Usage:** +- Multi-level marketing (MLM) tracking +- Regional affiliate tracking +- Sales team commission tracking +- Hierarchical partner structures + +**Subaffiliate Levels:** +- `subaffiliate1` / `sub1` - Primary sub-affiliate +- `subaffiliate2` / `sub2` - Secondary level +- `subaffiliate3` / `sub3` - Tertiary level +- `subaffiliate4` / `sub4` - Fourth level +- `subaffiliate5` / `sub5` - Fifth level + +## Click Tracking Parameters + +### Facebook Click ID (FBCLID) + +Facebook Ads click identifier for conversion tracking. + +**Parameter:** `fbclid` + +**Example:** +``` +https://yoursite.com/landing?fbclid=IwAR1X... +``` + +**Usage:** +- Facebook Ads conversion tracking +- Automatically appended by Facebook to ad links +- Stored in attribution metadata + +### Everflow Click ID (EVCLID) + +Everflow affiliate network tracking identifier. + +**Parameter:** `evclid` + +**Example:** +``` +https://yoursite.com/offer?evclid=123456789 +``` + +**Usage:** +- Everflow affiliate network tracking +- Performance marketing attribution +- Stored as `everflow_transaction_id` in metadata + +### SG Everflow Click ID + +Secondary Everflow tracking identifier (SG variant). + +**Parameter:** `sg_evclid` + +**Example:** +``` +https://yoursite.com/offer?sg_evclid=987654321 +``` + +**Usage:** +- Alternate Everflow tracking +- Multi-channel Everflow campaigns + +### Generic Click ID + +Generic click tracking for various platforms. + +**Parameter:** `clickid` + +**Example:** +``` +https://yoursite.com/product?clickid=abc123xyz +``` + +**Usage:** +- Custom tracking platforms +- Generic click tracking systems +- Platform-agnostic click attribution + +## How Attribution Works + +### 1. Automatic Capture + +When a user visits any page with attribution parameters: + +``` +https://yoursite.com/landing?funnel=summer-sale&affid=partner123&utm_source=facebook +``` + +The SDK automatically: +- Captures all attribution parameters +- Stores them in sessionStorage +- Logs the capture event (visible with debug mode) + +### 2. Session Persistence + +Once captured, attribution data persists throughout the session: + +``` +Page 1: /landing?funnel=summer-sale&affid=partner123 +Page 2: /products (no params) +Page 3: /checkout (no params) +``` + +All attribution data from Page 1 remains available on Pages 2 and 3. + +### 3. Parameter Override + +URL parameters **always override** existing persisted values: + +``` +Visit 1: ?funnel=spring-sale + → Sets funnel to "spring-sale" + +Visit 2: ?funnel=summer-sale + → Overrides funnel to "summer-sale" + → Logs: "🔄 Funnel override: 'spring-sale' -> 'summer-sale'" +``` + +### 4. Order Inclusion + +All captured attribution data is automatically included in: +- Cart creation API calls +- Checkout submission +- Order completion +- Upsell additions + +## JavaScript API + +### Get Current Attribution + +```javascript +// Get all attribution data +const attribution = next.getAttribution(); +console.log(attribution); +// { +// funnel: 'summer-sale', +// affiliate: 'partner123', +// utm_source: 'facebook', +// utm_campaign: 'summer-2024', +// metadata: { ... } +// } +``` + +### Set Attribution Programmatically + +```javascript +// Set specific attribution fields +next.setAttribution({ + funnel: 'vip-funnel', + affiliate: 'special-partner' +}); +``` + +### Add Custom Metadata + +```javascript +// Add custom tracking data +next.addMetadata('custom_field', 'custom_value'); + +// Set multiple metadata fields +next.setMetadata({ + landing_variant: 'A', + offer_code: 'SUMMER50', + sales_rep: 'john-smith' +}); +``` + +### Debug Attribution + +```javascript +// View all attribution data in console +next.debugAttribution(); + +// Output includes: +// - All attribution fields +// - UTM parameters +// - Click tracking IDs +// - Metadata +// - Timestamps +``` + +## Common Use Cases + +### 1. Affiliate Marketing Campaign + +``` +https://yoursite.com/offer?affid=partner123&subaffiliate1=region-west&subaffiliate2=agent-456 +``` + +Tracks: +- Affiliate partner +- Region/territory +- Individual sales agent + +### 2. Facebook Ad Campaign + +``` +https://yoursite.com/product?funnel=fb-summer-campaign&utm_source=facebook&utm_medium=cpc&utm_campaign=summer-2024&fbclid=IwAR1X... +``` + +Tracks: +- Campaign funnel +- UTM parameters for analytics +- Facebook click ID for conversion tracking + +### 3. Google Ads Campaign + +``` +https://yoursite.com/landing?funnel=google-search&utm_source=google&utm_medium=cpc&utm_campaign=brand-terms&gclid=EAIaIQobChMI7... +``` + +Tracks: +- Search campaign funnel +- UTM parameters +- Google click ID for conversion API + +### 4. Email Marketing + +``` +https://yoursite.com/exclusive?funnel=vip-email&utm_source=newsletter&utm_medium=email&utm_campaign=monthly-2024-06&utm_content=hero-cta +``` + +Tracks: +- Email funnel +- Newsletter source +- Specific email campaign +- Which CTA was clicked + +### 5. Multi-Tier Affiliate Program + +``` +https://yoursite.com/product?affid=network1&sub1=master-affiliate&sub2=sub-affiliate&sub3=sales-rep&sub4=region&sub5=channel +``` + +Tracks complete affiliate hierarchy for commission distribution. + +## Meta Tag Support + +Attribution can also be set via meta tags (URL parameters take priority): + +```html + + + + + +``` + +**Priority Order:** +1. URL parameters (highest priority, always override) +2. Persisted sessionStorage values +3. Meta tags (lowest priority) + +## Best Practices + +### 1. Use Consistent Naming + +```javascript +// Good - descriptive and consistent +?funnel=summer-sale-2024&affid=partner-network-west + +// Avoid - unclear abbreviations +?f=ss24&a=pnw +``` + +### 2. Combine Parameters Logically + +```javascript +// Track complete campaign attribution +?funnel=fb-campaign&affid=social-partner&utm_source=facebook&utm_medium=cpc&utm_campaign=summer-2024 +``` + +### 3. Document Your Funnels + +Maintain a list of active funnels and their purposes: +- `summer-sale-2024` - Summer promotional campaign +- `vip-exclusive` - VIP member offers +- `affiliate-special` - Affiliate partner promotions + +### 4. Test Attribution Capture + +```javascript +// In console, verify attribution was captured +next.debugAttribution(); + +// Check specific fields +const attribution = next.getAttribution(); +console.log('Funnel:', attribution.funnel); +console.log('Affiliate:', attribution.affiliate); +``` + +### 5. Monitor Attribution Data + +Use your backend analytics to: +- Track conversion by funnel +- Calculate affiliate ROI +- Optimize campaign performance +- Identify top-performing sources + +## Debugging + +### View Current Attribution + +```javascript +// Complete attribution debug +next.debugAttribution(); + +// Get attribution for API +const apiAttribution = next.getAttribution(); +console.log(JSON.stringify(apiAttribution, null, 2)); +``` + +### Check Stored Values + +```javascript +// Check sessionStorage directly +const stored = sessionStorage.getItem('next-attribution'); +console.log(JSON.parse(stored)); + +// Check specific persisted values +console.log('Funnel:', sessionStorage.getItem('next_funnel_name')); +console.log('EVCLID:', localStorage.getItem('evclid')); +``` + +### Enable SDK Debug Mode + +``` +https://yoursite.com/page?debug=true +``` + +Debug mode logs all attribution capture events to the console. + +## Important Notes + +### Character Limits + +- **Subaffiliate values**: Limited to 225 characters each +- Values exceeding this limit are automatically truncated with a warning + +### Persistence + +- Attribution data persists in **sessionStorage** (cleared when browser closes) +- Some values (funnel, evclid) also persist in **localStorage** (survives browser restart) + +### Security + +- Attribution parameters are visible in URLs +- Do not use attribution parameters for sensitive data +- Validate all attribution data server-side + +### API Integration + +- Attribution data is automatically included in all order-related API calls +- No manual intervention required +- Data appears in order records for reporting + +## Implementation Details + +**Files:** +- `src/utils/attribution/AttributionCollector.ts` - Captures and processes attribution data +- `src/stores/attributionStore.ts` - Manages attribution state +- `src/core/NextCommerce.ts` - Provides JavaScript API + +**Storage:** +- sessionStorage key: `next-attribution` +- Individual keys: `next_funnel_name`, `evclid`, etc. + +## See Also + +- [Getting Started - URL Parameters](../getting-started/url-parameters.md) - Other URL parameter features +- [API Reference - Attribution](../api-reference/attribution.md) - Complete attribution API +- [Configuration](../getting-started/configuration.md) - Using meta tags for configuration diff --git a/docs/campaign-cart/cart-system/buttons.md b/docs/campaign-cart/cart-system/buttons.md new file mode 100644 index 0000000..cb9f864 --- /dev/null +++ b/docs/campaign-cart/cart-system/buttons.md @@ -0,0 +1,126 @@ +# Cart Buttons + +Various button types for managing cart items. + +## Direct Add to Cart Buttons + +Buttons that add specific packages directly without selection. + +### Basic Add to Cart + +```html + +``` + +### Add and Redirect + +```html + +``` + +### Clear Cart, Add, and Redirect + +```html + +``` + +## Cart Toggle Buttons + +Toggle items in/out of cart with dynamic states. + +### Basic Toggle + +```html + +``` + +### Toggle with Quantity + +```html + +``` + +### Dynamic Text Toggle + +```html + +``` + +## State Container Pattern + +Container element gets state classes when item is in cart: + +```html +
+ + +
+``` + +## Quantity Sync Feature + +Perfect for warranties and accessories that should match main product quantity. + +```html + + +``` + +## Button Attributes + +### Add to Cart Attributes +- `data-next-action="add-to-cart"` - Action type +- `data-next-package-id` - Package to add +- `data-next-quantity` - Quantity to add +- `data-next-url` - Redirect after add +- `data-next-clear-cart` - Clear cart before adding + +### Toggle Attributes +- `data-next-toggle` - Enable toggle functionality +- `data-next-package-id` - Package to toggle +- `data-next-quantity` - Quantity when toggled on +- `data-add-text` - Text when item not in cart +- `data-remove-text` - Text when item in cart +- `data-next-is-upsell` - Mark as upsell item +- `data-next-package-sync` - Sync quantity with other packages + +## CSS Classes + +Toggle buttons and containers get automatic classes: +- `.next-in-cart` - Item is in cart +- `.next-active` - Toggle is active +- `.next-disabled` - Button is disabled \ No newline at end of file diff --git a/docs/campaign-cart/cart-system/index.md b/docs/campaign-cart/cart-system/index.md new file mode 100644 index 0000000..3f0d2ff --- /dev/null +++ b/docs/campaign-cart/cart-system/index.md @@ -0,0 +1,26 @@ +--- +sidebar_label: Cart System +sidebar_position: 1 +--- + +# Cart System + +Learn how to manage shopping carts with Campaign Cart JS SDK. + +## Overview + +- [Cart System Overview](/docs/campaign-cart/cart-system/overview) - Introduction to cart management + +## Components + +- [Selectors](/docs/campaign-cart/cart-system/selectors) - Product selection patterns +- [Buttons](/docs/campaign-cart/cart-system/buttons) - Add to cart and toggle buttons +- [Quantity Controls](/docs/campaign-cart/cart-system/quantity-controls) - Managing quantities +- [State Management](/docs/campaign-cart/cart-system/state-management) - Cart state and persistence + +## Related + +- [Attributes - Cart](/docs/campaign-cart/attributes/cart) - Display cart data +- [Upsells](/docs/campaign-cart/upsells/overview) - Post-purchase flows +- [Examples](/docs/campaign-cart/examples/basic-product-page) - Complete implementations + diff --git a/docs/campaign-cart/cart-system/overview.md b/docs/campaign-cart/cart-system/overview.md new file mode 100644 index 0000000..da748ae --- /dev/null +++ b/docs/campaign-cart/cart-system/overview.md @@ -0,0 +1,55 @@ +# Cart System Overview + +The Next Commerce JS SDK provides a flexible cart management system using HTML attributes. + +## Core Concepts + +### Cart Selectors +Interactive product selection components that allow users to choose products before adding to cart. + +### Selection Modes +- **Swap Mode**: Clicking a card immediately replaces cart contents +- **Select Mode**: User selects first, then clicks a button to add + +### Cart Actions +- Add to cart +- Remove from cart +- Toggle items +- Clear cart +- Update quantities + +### State Management +The cart state is automatically synchronized across all elements displaying cart data. + +## Key Features + +1. **Attribute-Driven**: Build cart functionality using HTML attributes +2. **Real-time Updates**: Cart displays update automatically +3. **Flexible Controls**: Multiple ways to add/remove items +4. **State Classes**: CSS classes automatically applied based on cart state + +## Basic Example + +```html + +
+
+ Product 1 +
+
+ Product 2 +
+
+ + +
+ Total: $0.00 +
+``` + +## Learn More + +- [Selectors](selectors.md) - Product selection patterns +- [Buttons](buttons.md) - Add to cart and toggle buttons +- [Quantity Controls](quantity-controls.md) - Managing quantities +- [State Management](state-management.md) - Cart state and persistence \ No newline at end of file diff --git a/docs/campaign-cart/cart-system/quantity-controls.md b/docs/campaign-cart/cart-system/quantity-controls.md new file mode 100644 index 0000000..0cb7f38 --- /dev/null +++ b/docs/campaign-cart/cart-system/quantity-controls.md @@ -0,0 +1,100 @@ +# Quantity Controls + +Manage product quantities in the cart with various control patterns. + +## Direct Quantity in Buttons + +Set quantity directly in add to cart buttons: + +```html + + +``` + +## Toggle with Quantity + +Toggle buttons can specify quantity: + +```html + +``` + +## Container-Based Quantity + +Specify quantity at the container level: + +```html +
+ +
+``` + +## Quantity Sync Feature + +Sync quantities between related products (e.g., warranties that match product quantity): + +```html + + + + + + + +``` + +## Quantity Display + +Show current quantity in cart: + +```html + +0 + + +0 +``` + +## Quantity-Based Conditionals + +Show/hide elements based on quantity: + +```html + +
+ You qualify for bulk discount! +
+ + +
Single item in cart
+
Multiple items in cart
+``` + +## Best Practices + +1. **Clear Labels**: Always indicate quantity in button text +2. **Visual Feedback**: Update button text when quantity changes +3. **Sync Related Items**: Use quantity sync for accessories/warranties +4. **Display Current Quantity**: Show users current cart quantities + +## Advanced Patterns + +TODO: Add information about: +- Quantity increment/decrement controls +- Input fields for quantity +- Maximum quantity limits +- Inventory-based restrictions \ No newline at end of file diff --git a/docs/campaign-cart/cart-system/selectors.md b/docs/campaign-cart/cart-system/selectors.md new file mode 100644 index 0000000..07d825c --- /dev/null +++ b/docs/campaign-cart/cart-system/selectors.md @@ -0,0 +1,102 @@ +# Cart Selectors + +Cart selectors allow users to choose products before adding them to cart. The SDK supports multiple selection patterns. + +## Swap Mode Selector + +In swap mode, clicking a card immediately replaces the current item in the cart. No button needed. + +```html +
+
+

Basic Package

+ $99 +
+
+

Premium Package

+ $199 +
+
+``` + +### Key Attributes +- `data-next-cart-selector`: Defines the selector container +- `data-next-selection-mode="swap"`: Auto-adds to cart on selection +- `data-next-selector-card`: Individual selectable cards +- `data-next-package-id`: Package ID to add +- `data-next-selected="true"`: Default selection + +## Select Mode with Button + +Select first, then click button to add. Button is disabled until selection is made. + +```html +
+
+

Package Option 1

+
+
+

Package Option 2

+
+
+ + +``` + +### Key Attributes +- `data-next-selection-mode="select"`: Requires button to add +- `data-next-selector-id`: Links selector to button +- `data-next-action="add-to-cart"`: Add to cart action + +## Displaying Selection Data + +Show data about the currently selected package: + +```html +
+ +
+ + +
+ Selected: None + Price: $0 +
+``` + +## CSS Classes + +Selectors automatically get CSS classes: +- `.next-selected` - Applied to selected card +- `.next-selector-active` - Applied when selector has selection + +## Advanced Features + +### Multiple Selectors +You can have multiple selectors on the same page with different IDs: + +```html + +
+ +
+ + +
+ +
+``` + +### Conditional Display +Show/hide elements based on selection: + +```html +
+ You've selected a package! +
+``` \ No newline at end of file diff --git a/docs/campaign-cart/cart-system/state-management.md b/docs/campaign-cart/cart-system/state-management.md new file mode 100644 index 0000000..d4de8dd --- /dev/null +++ b/docs/campaign-cart/cart-system/state-management.md @@ -0,0 +1,122 @@ +# State Management + +The Next Commerce JS SDK automatically manages cart state and synchronizes it across all elements. + +## Automatic State Sync + +All elements displaying cart data automatically update when the cart changes: + +```html + +$0.00 +0 +
Cart has items!
+``` + +## CSS State Classes + +Elements automatically receive CSS classes based on state: + +### Toggle Button States +```html + +``` + +### Container States +```html +
+ + + + Product Info +
+``` + +### Selector States +```html +
+ + Package Option +
+``` + +## State-Based Styling + +Use CSS to style based on state: + +```css +/* Style selected cards */ +.next-selected { + border: 2px solid blue; + background: #f0f0ff; +} + +/* Style items in cart */ +.next-in-cart { + opacity: 0.6; +} + +/* Style active toggle buttons */ +button.next-active { + background: green; + color: white; +} +``` + +## Conditional Display + +Show/hide elements based on cart state: + +```html + +
Your cart is empty
+
+ You have 0 items +
+ + +
Package 2 is in your cart
+ + +
Free shipping unlocked!
+``` + +## Events + +Listen to state changes: + +```javascript +// Cart updated +next.on('cart:updated', (data) => { + console.log('Cart updated:', data); +}); + +// Item added +next.on('cart:item-added', (data) => { + console.log('Item added:', data); +}); + +// Item removed +next.on('cart:item-removed', (data) => { + console.log('Item removed:', data); +}); +``` + +## State Persistence + +TODO: Add information about: +- Cart persistence across page loads +- Session management +- Cart expiration +- Cross-tab synchronization + +## Best Practices + +1. **Use CSS Classes**: Style based on state classes for better UX +2. **Conditional Content**: Show relevant messages based on cart state +3. **Listen to Events**: Trigger analytics or custom logic on state changes +4. **Loading States**: Use data-next-await for initial loading \ No newline at end of file diff --git a/docs/campaign-cart/checkout/index.md b/docs/campaign-cart/checkout/index.md new file mode 100644 index 0000000..76e35b5 --- /dev/null +++ b/docs/campaign-cart/checkout/index.md @@ -0,0 +1,18 @@ +--- +sidebar_label: Checkout +sidebar_position: 1 +--- + +# Checkout + +Checkout flow implementation and configuration. + +- [Multi-Step Implementation Example](/docs/campaign-cart/checkout/multi-step-implementation-example) - Multi-step checkout flow +- [Spreedly Configuration](/docs/campaign-cart/checkout/spreedly-configuration) - Spreedly payment integration + +## Related + +- [Examples - Checkout Page](/docs/campaign-cart/examples/checkout-page) - Complete checkout example +- [Attributes - Checkout](/docs/campaign-cart/attributes/checkout) - Checkout attributes +- [Cart System](/docs/campaign-cart/cart-system/overview) - Cart management + diff --git a/docs/campaign-cart/checkout/multi-step-implementation-example.md b/docs/campaign-cart/checkout/multi-step-implementation-example.md new file mode 100644 index 0000000..1f67a6e --- /dev/null +++ b/docs/campaign-cart/checkout/multi-step-implementation-example.md @@ -0,0 +1,249 @@ +# Multi-Step Checkout Implementation Example + +## Overview +The `CheckoutFormEnhancer` now supports multi-step checkout with step-based validation. Here's how to implement it: + +## Step 1: Information Page (information.html) + +### Key Changes Required: + +1. **Add form wrapper with step attributes**: + ```html +
+ ``` + +2. **Keep your existing fields** - all fields with `data-next-checkout-field` attributes will auto-save to the store + +3. **Button should remain type="submit"** - the enhancer will intercept and validate + +### Complete Example for information.html: + +```html + + + +
+

Contact

+
+ +
+
+ + +
+

Shipping

+ + +
+ +
+ + +
+ + + +
+ + + + + + + + +
+ + + + + +
+ + + +
+ + +
+ +
+
+``` + +## Step 2: Shipping Page (shipping.html) + +```html +
+ + +
+

Contact

+

+ Change +
+ +
+

Ship to

+

+ Change +
+ + +
+

Shipping Method

+ + +
+ + +
+ + +``` + +## Step 3: Payment Page (payment.html) + +```html +
+ + + +
+ +
+ + +
+
+ + + + +
+``` + +## How It Works + +1. **Automatic Field Saving**: All fields with `data-next-checkout-field` are automatically saved to `checkoutStore` on change +2. **Step Detection**: When form has `data-next-checkout-step` attribute, it's treated as a navigation step +3. **Step Validation**: Only validates fields required for current step (defined in `CheckoutValidator.validateStep()`) +4. **Data Persistence**: `checkoutStore` persists across page navigations (uses Zustand with localStorage) +5. **Final Step**: Page without `data-next-checkout-step` triggers full validation + order creation + +## Attributes Reference + +| Attribute | Required | Description | Example | +|-----------|----------|-------------|---------| +| `data-next-checkout` | Yes | Marks the form for CheckoutFormEnhancer (must be "form") | `
` | +| `data-next-checkout-step` | Step 1 & 2 | URL to navigate to after validation | `data-next-checkout-step="shipping.html"` | +| `data-next-step-number` | Optional | Current step number (default: 1) | `data-next-step-number="1"` | +| `data-next-checkout-field` | Yes | Field name for auto-save | `data-next-checkout-field="email"` | + +## Step 1 Required Fields + +The validator checks these fields for step 1: +- `email` +- `fname` +- `lname` +- `country` +- `address1` +- `city` +- `postal` +- `province` (if country requires it) +- `phone` (if marked as required in HTML) + +## Notes + +- Data is automatically persisted via `checkoutStore` +- No need for manual `checkoutStore.updateFormData()` - the enhancer handles it +- You can access stored data via `window.Next.stores.checkout.getState().formData` +- Phone validation uses intl-tel-input if available +- Postal code validation is country-specific diff --git a/docs/campaign-cart/checkout/spreedly-configuration.md b/docs/campaign-cart/checkout/spreedly-configuration.md new file mode 100644 index 0000000..1d4eff0 --- /dev/null +++ b/docs/campaign-cart/checkout/spreedly-configuration.md @@ -0,0 +1,309 @@ +# Spreedly iFrame Configuration + +Configure Spreedly's iFrame payment fields through `window.nextConfig`. All options are **optional** - the SDK works perfectly with defaults. + +## Basic Usage (No Config Needed) + +The SDK automatically loads the Spreedly environment key from your campaign API. No configuration needed: + +```javascript +// That's it! Spreedly works out of the box +``` + +## Custom Configuration (Optional) + +Add custom Spreedly settings to control field types, styling, security, and more: + +```javascript +window.nextConfig = { + apiKey: 'your-api-key', + + // Optional Spreedly configuration + spreedly: { + // Field types (controls mobile keyboards) + fieldType: { + number: 'tel', // Options: 'text' | 'tel' | 'number' + cvv: 'number' // Options: 'text' | 'tel' | 'number' + }, + + // Number format + numberFormat: 'prettyFormat', // 'prettyFormat' | 'plainFormat' | 'maskedFormat' + + // Custom placeholders + placeholders: { + number: 'Enter Card Number', + cvv: 'CVV' + }, + + // Custom styling (CSS string) + styles: { + number: 'font-size: 16px; color: #333; padding: 12px;', + cvv: 'font-size: 16px; color: #333; padding: 12px;', + placeholder: 'color: #999; font-style: italic;' + }, + + // Security parameters (for enhanced authentication) + nonce: 'unique-session-uuid', // Generated per session + timestamp: '1738252535', // Epoch time + certificateToken: 'your-cert-token', // From Spreedly + signature: 'server-generated-signature', // SHA256 hash + + // Fraud detection + fraud: true, // or { siteId: 'your-fraud-site-id' } + + // Other options + enableAutoComplete: false, + allowBlankName: false, + allowExpiredDate: false + } +}; +``` + +## Configuration Options + +### Field Types + +Control keyboard display on mobile devices: + +```javascript +spreedly: { + fieldType: { + number: 'tel', // Shows telephone keypad + cvv: 'number' // Shows numeric keypad + } +} +``` + +**Options**: `'text'`, `'tel'`, `'number'` + +### Number Format + +```javascript +spreedly: { + numberFormat: 'prettyFormat' // 4111 1111 1111 1111 + // numberFormat: 'plainFormat' // 4111111111111111 + // numberFormat: 'maskedFormat' // **************** +} +``` + +### Placeholders + +```javascript +spreedly: { + placeholders: { + number: 'Card Number', + cvv: 'Security Code' + } +} +``` + +### Labels (Accessibility) + +```javascript +spreedly: { + labels: { + number: 'Credit Card Number', + cvv: 'CVV Code' + } +} +``` + +### Titles (Accessibility) + +```javascript +spreedly: { + titles: { + number: 'Enter your card number', + cvv: 'Enter security code' + } +} +``` + +### Custom Styling + +Apply CSS to iFrame fields: + +```javascript +spreedly: { + styles: { + number: 'font-size: 18px; color: #000; font-family: Arial;', + cvv: 'font-size: 18px; color: #000;', + placeholder: 'color: #aaa; font-weight: 300;' + } +} +``` + +### Security Parameters + +For Spreedly's enhanced security (optional): + +```javascript +spreedly: { + nonce: generateUUID(), // Unique per session + timestamp: Math.floor(Date.now() / 1000).toString(), + certificateToken: 'your-certificate-token', + signature: generateSignatureOnServer() // Server-generated +} +``` + +**Note**: The signature must be generated server-side using your private key. + +### Fraud Detection + +Enable fraud prevention: + +```javascript +spreedly: { + fraud: true // Uses default fraud detection +} + +// OR with custom fraud site + +spreedly: { + fraud: { siteId: 'your-fraud-site-id' } // BYOC fraud +} +``` + +### Validation Options + +```javascript +spreedly: { + allowBlankName: false, // Skip name validation + allowExpiredDate: false, // Allow expired cards + enableAutoComplete: false // Disable autocomplete +} +``` + +### Required Attributes + +Control HTML `required` attribute: + +```javascript +spreedly: { + requiredAttributes: { + number: true, // Default + cvv: true // Default + } +} +``` + +## Alternative Naming + +You can use either `spreedly` or `spreedlyConfig` key: + +```javascript +window.nextConfig = { + spreedly: { /* config */ } + // OR + spreedlyConfig: { /* config */ } +}; +``` + +## Default Values + +If you don't provide configuration, these defaults are used: + +```javascript +{ + fieldType: { + number: 'text', + cvv: 'text' + }, + numberFormat: 'prettyFormat', + placeholders: { + number: 'Card Number', + cvv: 'CVV *' + }, + styles: { + number: 'color: #212529; font-size: .925rem; font-weight: 400; width: 100%; height:100%;', + cvv: 'color: #212529; font-size: .925rem; font-weight: 400; width: 100%; height:100%;' + }, + requiredAttributes: { + number: true, + cvv: true + }, + enableAutoComplete: true, + allowBlankName: false, + allowExpiredDate: false +} +``` + +## Complete Example + +```html + + + + + + + + + + + + +
+
+ + +
+ + +``` + +## Debugging + +Enable debug logging to see applied configuration: + +```javascript +window.nextConfig = { + debug: true, + spreedly: { /* your config */ } +}; +``` + +Check browser console for: +``` +[CreditCardService] Spreedly configuration applied: { + fieldType: { number: 'tel', cvv: 'number' }, + numberFormat: 'prettyFormat', + ... +} +``` + +## Related Documentation + +- [Spreedly iFrame API Docs](https://docs.spreedly.com/reference/iframe/v1/) +- [Checkout Configuration](../getting-started/configuration.md) \ No newline at end of file diff --git a/docs/campaign-cart/examples/advanced-customization.md b/docs/campaign-cart/examples/advanced-customization.md new file mode 100644 index 0000000..9f482fb --- /dev/null +++ b/docs/campaign-cart/examples/advanced-customization.md @@ -0,0 +1,625 @@ +# Advanced Customization Examples + +Complex scenarios and edge cases with the Next Commerce JS SDK. + +## Dynamic Bundle Builder + +Build custom bundles with real-time pricing updates: + +```html + + + + + Build Your Custom Drone Bundle + + + + + + + + +
+
+ +
+

1. Choose Your Drone

+
+
+ +
+

Drone Pro X Basic

+

Entry-level drone with HD camera

+
+
$599
+
+ +
+ +
+

Drone Pro X Advanced

+

4K camera with gimbal stabilization

+
+
$899
+
+ +
+ +
+

Drone Pro X Ultimate

+

8K camera, obstacle avoidance, 45min flight

+
+
$1299
+
+
+
+ + +
+

2. Extra Batteries

+
+
+
+

No Extra Batteries

+

Just the one that comes with drone

+
+
$0
+
+ +
+
+

+1 Extra Battery

+

Double your flight time

+
+
$79
+
+ +
+
+

+2 Extra Batteries

+

Triple your flight time

+ + Save 10% + +
+
$149
+
+
+
+ + +
+

3. Select Accessories

+
+
+ +
+

Carrying Case

+

Professional hard-shell case

+
+
$49
+
+ +
+ +
+

ND Filter Set

+

6 filters for perfect exposure

+
+
$39
+
+ +
+ +
+

Extra Propellers

+

2 complete sets

+
+
$29
+
+ +
+ +
+

Landing Pad

+

Professional takeoff/landing surface

+
+
$19
+
+
+
+ + +
+

4. Protection Plan

+
+
+
+

No Protection

+

Standard manufacturer warranty only

+
+
$0
+
+ +
+
+

2-Year Protection

+

Covers accidents and defects

+
+
$99
+
+ +
+
+

3-Year Protection + Care

+

Full coverage + annual maintenance

+
+
$179
+
+
+
+
+ + +
+

Your Bundle

+ + +
+
+ Drone: + $0 +
+ +
+ Batteries: + $0 +
+ +
+ Carrying Case: $49 +
+ +
+ ND Filter Set: $39 +
+ +
+ Extra Propellers: $29 +
+ +
+ Landing Pad: $19 +
+ +
+ Protection: + $0 +
+
+ +
+ + +
+ Total: $0 +
+ + +
+ You're saving $0! + That's 0% off retail. +
+ + +
+

Bundle Discounts

+
+ 3+ items: 5% off + ✓ Active +
+
+ 5+ items: 10% off + ✓ Active +
+
+ $1500+: Extra 5% off + ✓ Active +
+
+ + + + + +
+
+ + + + +``` + +## Multi-Currency Support + +Handle different currencies and regional pricing: + +```html + +
+ +
+ + +
+ $ + 99.99 +
+ + +``` + +## A/B Testing Implementation + +Test different layouts and messaging: + +```html + +``` + +## Dynamic Pricing Rules + +Implement complex pricing logic: + +```html + +``` + +## Custom Validation + +Add custom validation before checkout: + +```html + +``` + +## Performance Optimization + +Optimize for large catalogs: + +```html + +Product + + +
+ +
+ + +``` + +These advanced examples demonstrate: +1. **Dynamic Bundle Building** - Complex multi-step configuration +2. **Multi-Currency Support** - Regional pricing +3. **A/B Testing** - Conversion optimization +4. **Dynamic Pricing** - Time and quantity-based rules +5. **Custom Validation** - Business logic enforcement +6. **Performance Optimization** - Large catalog handling \ No newline at end of file diff --git a/docs/campaign-cart/examples/basic-product-page.md b/docs/campaign-cart/examples/basic-product-page.md new file mode 100644 index 0000000..19be547 --- /dev/null +++ b/docs/campaign-cart/examples/basic-product-page.md @@ -0,0 +1,314 @@ +# Basic Product Page Example + +Complete implementation of a product page with the Next Commerce JS SDK. + +## Full HTML Example + +```html + + + + + + Product Page - Drone Pro X + + + + + + + + + + + + + + +
+

Cart

+

Your cart is empty

+
+

Items: 0

+

Total: $0.00

+ Checkout +
+
+ + +
+ +
+ Drone Pro X +
+ + +
+

Drone Pro X - Professional Camera Drone

+ + +
+ +
+

Single Drone

+

$599

+

Perfect for beginners

+
+ + +
+

Pro Bundle (Most Popular)

+

$899

+

+ Save 25% + ($300) +

+
    +
  • 1x Drone Pro X
  • +
  • 2x Extra Batteries
  • +
  • Carrying Case
  • +
  • Extra Propellers
  • +
+
+ + +
+

Ultimate Bundle

+

$1299

+

+ Save 35% + ($700) +

+
    +
  • Everything in Pro Bundle
  • +
  • Advanced Controller
  • +
  • ND Filter Set
  • +
  • 1 Year Warranty
  • +
+
+
+ + + + + +
+

Recommended Accessories

+ + +
+ +
+ + +
+ +
+
+
+
+ + + + + +``` + +## Key Features Demonstrated + +1. **Package Selector** - Swap mode for instant cart updates +2. **Dynamic Pricing** - Prices update based on selection +3. **Savings Display** - Show discounts conditionally +4. **Accessories** - Toggle buttons for add-ons +5. **Cart Summary** - Real-time cart display +6. **FOMO Notifications** - Social proof +7. **Exit Intent** - Recover abandoning visitors +8. **Loading States** - Skeleton loading + +## Responsive Version + +Add these styles for mobile: + +```css +@media (max-width: 768px) { + .product-container { + grid-template-columns: 1fr; + } + + .cart-summary { + position: static; + margin-bottom: 20px; + } + + .package-card { + font-size: 14px; + } + + .add-to-cart { + position: sticky; + bottom: 20px; + z-index: 100; + } +} +``` + +## Analytics Integration + +```javascript +// Add Google Analytics tracking +next.on('cart:item-added', (data) => { + if (typeof gtag !== 'undefined') { + gtag('event', 'add_to_cart', { + currency: 'USD', + value: data.item.price, + items: [{ + item_id: data.item.packageId, + item_name: data.item.name, + price: data.item.price, + quantity: data.item.quantity + }] + }); + } +}); +``` \ No newline at end of file diff --git a/docs/campaign-cart/examples/checkout-page.md b/docs/campaign-cart/examples/checkout-page.md new file mode 100644 index 0000000..63b414e --- /dev/null +++ b/docs/campaign-cart/examples/checkout-page.md @@ -0,0 +1,590 @@ +# Checkout Page Example + +Complete checkout page implementation with cart summary, form validation, express checkout, and bump offers. + +## Full HTML Example + +```html + + + + + Checkout + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+
+
+
+ Show Order Summary +
+
+
+
+
$109.96
+
$109.96
+
+
+
+ +
+ +
+
+
+
+
+
+
{item.quantity}
+
+ +
+
+
+
+
{item.name}
+
One time purchase
+
+
+
{item.unitComparePrice}
+
{item.unitPrice}
+
+
+
+
+
+
+
+ + +
+
+
+
+ +
+ +
+
+ +
+
+
+ + +
+
+
+
Subtotal
+
$0.00
+
+
+ +
+
+
Shipping
+
FREE
+
+
+ +
+
+
+
Today You Saved
+
+ 0% OFF +
+
+
+
Discount:
+
$0.00
+
+
+
+ +
+
+
Grand Total:
+
+
USD
+
$0.00
+
+
+ +
+
+
+ TOTAL SAVINGS $0.00 +
+
+
+
+
+ + +
+
YOUR CART IS EMPTY
+ Continue Shopping +
+
+
+
+ + +
+
+
+
+
+
+
+
YES, I Will Take It
+
+
+
+
+ +
+ Get peace of mind with Shipping Guarantee + in the event your delivery is damaged, stolen, or lost during transit for + $1.99 +
+
+
+
+
+ + +
+
Express Checkout
+ +
+
+
Error message
+
+
+ +
+ + + +
+
+ + +
+
+
+ +
+

Contact Information

+
+ + +
+
+ + +
+

Shipping Information

+
+
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+
+
+ + +
+

Payment Details

+
All transactions are secure and encrypted.
+ +
+
+
+ +
+ +
+
+
+
Error message
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ + + +
+
+
+ + +``` + +## Key SDK Attributes Used + +### Cart Display & Management +- `data-next-cart-items=""` - Container for dynamic cart items +- `data-next-display="cart.total"` - Display cart total +- `data-next-display="cart.subtotal"` - Display subtotal +- `data-next-display="cart.shipping"` - Display shipping cost +- `data-next-display="cart.savingsAmount"` - Display savings amount +- `data-next-display="cart.totalSavingsAmount"` - Display total savings amount in currency +- `data-next-display="cart.totalSavingsPercentage"` - Display savings percentage +- `data-next-display="cart.compareTotal"` - Display original price before discounts +- `data-next-show="cart.hasItems"` - Show when cart has items +- `data-next-show="cart.isEmpty"` - Show when cart is empty +- `data-next-show="cart.hasSavings"` - Show when there are savings +- `data-next-show="cart.hasShipping"` - Show when shipping is applicable +- `data-next-hide="cart.isEmpty"` - Hide when cart is empty + +### Accordion Component +- `data-next-accordion="order-summary"` - Define accordion container +- `data-next-accordion-trigger="order-summary"` - Accordion trigger button +- `data-next-accordion-panel="order-summary"` - Accordion content panel +- `data-next-accordion-text="order-summary"` - Dynamic text for open/close states +- `data-initial-state="closed"` - Set initial accordion state +- `data-open-text="Hide Order Summary"` - Text when accordion is open +- `data-close-text="Show Order Summary"` - Text when accordion is closed +- `data-toggle-class="next-expanded"` - CSS class to toggle + +### Coupon Management +- `data-next-coupon="input"` - Coupon input configuration +- `data-next-coupon="display"` - Container for applied coupons +- `data-auto-apply="true"` - Auto-apply coupon on valid input +- `data-placeholder="Promo code (optional)"` - Input placeholder text +- `data-button-text="Apply"` - Button text + +### Bump Offers +- `data-next-bump=""` - Mark container as bump offer +- `data-next-package-id="6"` - Package ID for the bump offer +- `data-next-toggle="toggle"` - Enable toggle functionality +- `data-next-package-sync="2,3,4"` - Sync quantity with other packages + +### Express Checkout +- `data-next-express-checkout="container"` - Express checkout container +- `data-next-express-checkout="buttons"` - Button container +- `data-next-express-checkout="paypal"` - PayPal button +- `data-next-express-checkout="apple_pay"` - Apple Pay button +- `data-next-express-checkout="google_pay"` - Google Pay button + +### Checkout Form +- `data-next-checkout="form"` - Main checkout form +- `data-next-checkout-field="email"` - Email field +- `data-next-checkout-field="fname"` - First name field +- `data-next-checkout-field="lname"` - Last name field +- `data-next-checkout-field="address1"` - Address line 1 +- `data-next-checkout-field="address2"` - Address line 2 +- `data-next-checkout-field="city"` - City field +- `data-next-checkout-field="country"` - Country selector +- `data-next-checkout-field="province"` - State/Province selector +- `data-next-checkout-field="postal"` - ZIP/Postal code +- `data-next-checkout-field="phone"` - Phone number +- `data-next-checkout-field="cc-number"` - Credit card number (secure field) +- `data-next-checkout-field="exp-month"` - Credit card expiration month +- `data-next-checkout-field="exp-year"` - Credit card expiration year +- `data-next-checkout-field="cvv"` - Credit card CVV code + +### Payment Methods +- `data-next-payment-method="credit"` - Credit card payment method +- `data-next-payment-method="paypal"` - PayPal payment method +- `data-next-payment-form="credit"` - Credit card form container +- `data-next-payment-form="paypal"` - PayPal form container + +### Error Handling +- `data-next-component="express-error"` - Express checkout error container +- `data-next-component="express-error-text"` - Express checkout error message +- `data-next-component="credit-error"` - Credit card error container +- `data-next-component="credit-error-text"` - Credit card error message +- `data-next-component="paypal-error"` - PayPal error container +- `data-next-component="paypal-error-text"` - PayPal error message + +### Loading States +- `data-next-await=""` - Show loading skeleton while data loads +- `data-next-skeleton=""` - Skeleton loading indicator + +### Other Utilities +- `data-next-content=""` - Mark dynamic content area +- `data-animate="right"` - Animation direction for right arrow +- `data-animate="left"` - Animation direction for left arrow +- `data-next-component="shipping-field-row"` - Container for shipping form field rows +- `data-next-component="scroll-hint"` - Scroll hint for long cart item lists +- `data-next-tooltip=""` - Add tooltip to form fields +- `data-checkout-tooltip=""` - Tooltip content +- `data-checkout-tooltip-position="top"` - Tooltip position +- `data-template="tag"` - Template type for coupon display +- `data-action="submit"` - Action trigger for buttons + +## Features Demonstrated + +1. **Mobile-First Design** - Accordion for order summary on mobile +2. **Dynamic Cart Display** - Real-time cart items with quantity badges +3. **Coupon System** - Apply and display discount codes with auto-apply option +4. **Bump Offers** - Pre-checkout upsell with toggle functionality and package syncing +5. **Express Checkout** - PayPal, Apple Pay, Google Pay options +6. **Form Validation** - Required fields, pattern validation, and autocomplete attributes +7. **Secure Payment** - PCI-compliant credit card fields with Spreedly integration +8. **Loading States** - Skeleton screens while data loads +9. **Error Handling** - Toast notifications for errors across payment methods +10. **Conditional Display** - Show/hide based on cart state +11. **Scroll Hints** - Visual indicators for scrollable cart items +12. **Multiple Payment Methods** - Credit card and PayPal options +13. **Billing Address** - Option to use shipping address as billing +14. **Field Tooltips** - Help text for form fields +15. **Security Indicators** - Visual security badges and messaging + +## Security Note + +The SDK handles all sensitive payment data securely: +- Credit card fields are tokenized using Spreedly +- No sensitive data is stored client-side +- All transactions are encrypted (100% Encrypted & Secure Checkout) +- PCI compliance is maintained +- Secure field indicators with lock icons +- SSL/TLS encryption for all data transmission + +## Additional Implementation Details + +### Meta Tags +Required meta tags for checkout functionality: +```html + + + +``` + +### CSS Classes +The SDK automatically adds these classes: +- `next-selected` - Applied to selected payment methods +- `next-active` - Applied to active bump offers +- `next-expanded` - Applied when accordion is expanded +- `next-disabled` - Applied to disabled buttons +- `next-in-cart` - Applied to items in cart +- `next-display-ready` - Applied to HTML when SDK is ready + +### Form Validation Patterns +- ZIP Code: `pattern="(^\d{5}$)|(^\d{5}-\d{4}$)"` +- City: `pattern="^[A-Za-z\s]+$"` +- Phone: Uses international tel input library + +### Autocomplete Support +All form fields include proper autocomplete attributes: +- `autocomplete="email"` +- `autocomplete="given-name"` +- `autocomplete="family-name"` +- `autocomplete="address-line1"` +- `autocomplete="address-line2"` +- `autocomplete="address-level2"` (city) +- `autocomplete="address-level1"` (state) +- `autocomplete="postal-code"` +- `autocomplete="tel"` +- `autocomplete="cc-number"` +- `autocomplete="cc-exp-month"` +- `autocomplete="cc-exp-year"` \ No newline at end of file diff --git a/docs/campaign-cart/examples/index.md b/docs/campaign-cart/examples/index.md new file mode 100644 index 0000000..00ccf17 --- /dev/null +++ b/docs/campaign-cart/examples/index.md @@ -0,0 +1,27 @@ +--- +sidebar_label: Examples +sidebar_position: 1 +--- + +# Examples + +Complete implementations and code examples for common use cases. + +## Product Pages + +- [Basic Product Page](/docs/campaign-cart/examples/basic-product-page) - Simple product page +- [Checkout Page](/docs/campaign-cart/examples/checkout-page) - Checkout implementation + +## Advanced Flows + +- [Upsell Flow](/docs/campaign-cart/examples/upsell-flow) - Multi-step upsells +- [Advanced Customization](/docs/campaign-cart/examples/advanced-customization) - Complex scenarios +- [Tier Selector Implementation](/docs/campaign-cart/examples/tier-selector-implementation) - Tier selection patterns +- [Quantity Package Swapper](/docs/campaign-cart/examples/quantity-package-swapper) - Quantity-based swapping + +## Related + +- [Getting Started - Quick Start](/docs/campaign-cart/getting-started/quick-start) - Basic examples +- [Cart System](/docs/campaign-cart/cart-system/overview) - Cart management +- [Upsells](/docs/campaign-cart/upsells/overview) - Upsell flows + diff --git a/docs/campaign-cart/examples/quantity-package-swapper-example.html b/docs/campaign-cart/examples/quantity-package-swapper-example.html new file mode 100644 index 0000000..380c783 --- /dev/null +++ b/docs/campaign-cart/examples/quantity-package-swapper-example.html @@ -0,0 +1,86 @@ + + + + + + Quantity Package Swapper Example + + + + +

Quantity Package Swapper

+ + +
+
+ + +

Loading...

+

Price: $0

+ + +
+ + 1 + +
+ +

Total: $0

+
+
+ + + + + + + + + diff --git a/docs/campaign-cart/examples/quantity-package-swapper.js b/docs/campaign-cart/examples/quantity-package-swapper.js new file mode 100644 index 0000000..09d2a0f --- /dev/null +++ b/docs/campaign-cart/examples/quantity-package-swapper.js @@ -0,0 +1,224 @@ +(function() { + 'use strict'; + + const QUANTITY_MAPS = { + 'limos-card': { + 1: { packageId: 2, quantity: 1 }, + 2: { packageId: 3, quantity: 2 }, + 3: { packageId: 4, quantity: 1 }, + 4: [ + { packageId: 2, quantity: 2 }, + { packageId: 3, quantity: 1 } + ] + } + }; + + const CONFIG = { + debug: true, + clearCartOnLoad: true, + resetToQuantityOne: true, + addInitialPackage: true // Add the quantity 1 package to cart on load + }; + + const reverseMaps = {}; + let isInitialized = false; + + function log(...args) { + if (CONFIG.debug) console.log('[QuantitySwapper]', ...args); + } + + function normalizePackageDefinition(definition, selectorQuantity) { + if (Array.isArray(definition)) return definition; + if (typeof definition === 'number') return [{ packageId: definition, quantity: selectorQuantity }]; + if (typeof definition === 'object' && definition.packageId) return [definition]; + return []; + } + + function buildReverseMaps() { + Object.keys(QUANTITY_MAPS).forEach(selectorId => { + reverseMaps[selectorId] = {}; + Object.entries(QUANTITY_MAPS[selectorId]).forEach(([qty, definition]) => { + const normalized = normalizePackageDefinition(definition, parseInt(qty)); + if (normalized.length > 0) { + reverseMaps[selectorId][normalized[0].packageId] = parseInt(qty); + } + }); + }); + } + + function getSelector(selectorId) { + return document.querySelector(`[data-qty-selector="${selectorId}"]`); + } + + function getSelectorCard(selectorId) { + const selector = getSelector(selectorId); + return selector ? selector.querySelector('[data-qty-card]') : null; + } + + async function swapToPackages(packages) { + try { + await next.swapCart(packages); + log('✅ Cart swapped'); + return true; + } catch (err) { + console.error('[QuantitySwapper] Failed to swap cart:', err); + return false; + } + } + + async function clearCart() { + try { + await next.clearCart(); + log('✅ Cart cleared'); + return true; + } catch (err) { + console.error('[QuantitySwapper] Failed to clear cart:', err); + return false; + } + } + + function resetSelectorToQuantityOne(selectorId) { + const card = getSelectorCard(selectorId); + if (!card) return; + + card.setAttribute('data-qty-current', '1'); + const display = card.querySelector('[data-qty-display]'); + if (display) display.textContent = '1'; + + const firstPackageDef = QUANTITY_MAPS[selectorId][1]; + if (firstPackageDef) { + const packages = normalizePackageDefinition(firstPackageDef, 1); + if (packages.length > 0) { + card.setAttribute('data-next-package-id', packages[0].packageId); + } + } + + log(`Reset ${selectorId} to quantity 1`); + } + + function setupQuantityControls(selectorId) { + const selector = getSelector(selectorId); + if (!selector) return; + + const card = selector.querySelector('[data-qty-card]'); + if (!card) return; + + const increaseBtn = card.querySelector('[data-qty-increase]'); + const decreaseBtn = card.querySelector('[data-qty-decrease]'); + const displayElement = card.querySelector('[data-qty-display]'); + + if (!increaseBtn || !decreaseBtn || !displayElement) return; + + const minQuantity = parseInt(card.getAttribute('data-qty-min') || '1', 10); + const maxQuantity = parseInt(card.getAttribute('data-qty-max') || '999', 10); + + let currentQuantity = parseInt(card.getAttribute('data-qty-current') || '1', 10); + + const updateButtonStates = () => { + const isAtMin = currentQuantity <= minQuantity; + const isAtMax = currentQuantity >= maxQuantity; + + decreaseBtn.disabled = isAtMin; + decreaseBtn.classList.toggle('next-disabled', isAtMin); + + increaseBtn.disabled = isAtMax; + increaseBtn.classList.toggle('next-disabled', isAtMax); + }; + + const handleQuantityChange = async (newQuantity) => { + currentQuantity = newQuantity; + displayElement.textContent = currentQuantity.toString(); + card.setAttribute('data-qty-current', currentQuantity.toString()); + updateButtonStates(); + + log(`Quantity changed: selector=${selectorId}, qty=${currentQuantity}`); + + const packageMap = QUANTITY_MAPS[selectorId]; + if (!packageMap) return; + + const packageDefinition = packageMap[currentQuantity]; + if (!packageDefinition) return; + + const targetPackages = normalizePackageDefinition(packageDefinition, currentQuantity); + if (targetPackages.length === 0) return; + + const targetPackageId = targetPackages[0].packageId; + card.setAttribute('data-next-package-id', targetPackageId); + + log(`Swapping to package ${targetPackageId}`); + await swapToPackages(targetPackages); + }; + + increaseBtn.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + if (currentQuantity < maxQuantity) { + handleQuantityChange(currentQuantity + 1); + } + }); + + decreaseBtn.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + if (currentQuantity > minQuantity) { + handleQuantityChange(currentQuantity - 1); + } + }); + + updateButtonStates(); + log(`✅ Quantity controls setup for ${selectorId}`); + } + + async function initialize() { + if (isInitialized) return; + + log('Initializing...'); + + if (CONFIG.clearCartOnLoad) { + await clearCart(); + } + + buildReverseMaps(); + + if (CONFIG.resetToQuantityOne) { + Object.keys(QUANTITY_MAPS).forEach(selectorId => { + resetSelectorToQuantityOne(selectorId); + }); + } + + if (CONFIG.addInitialPackage) { + Object.keys(QUANTITY_MAPS).forEach(selectorId => { + const firstPackageDef = QUANTITY_MAPS[selectorId][1]; + if (firstPackageDef) { + const packages = normalizePackageDefinition(firstPackageDef, 1); + if (packages.length > 0) { + swapToPackages(packages); + log(`Added initial package(s) for ${selectorId}`); + } + } + }); + } + + Object.keys(QUANTITY_MAPS).forEach(selectorId => { + setupQuantityControls(selectorId); + }); + + isInitialized = true; + log('✅ Initialized'); + } + + if (typeof window !== 'undefined') { + window.addEventListener('next:initialized', function() { + setTimeout(initialize, 50); + }); + + window.QuantityPackageSwapper = { + initialize, + swapToPackages, + clearCart, + config: CONFIG, + maps: QUANTITY_MAPS + }; + } + +})(); diff --git a/docs/campaign-cart/examples/quantity-package-swapper.md b/docs/campaign-cart/examples/quantity-package-swapper.md new file mode 100644 index 0000000..e6384b9 --- /dev/null +++ b/docs/campaign-cart/examples/quantity-package-swapper.md @@ -0,0 +1,348 @@ +# Quantity-Based Package Swapper (Standalone) + +A fully standalone script that handles quantity controls and package swapping. **No SDK selector enhancer needed** - this script manages everything directly. + +## Features + +- ✅ **Fully Standalone**: Handles its own quantity controls, no SDK selector enhancer +- ✅ **Zero Conflicts**: Doesn't interfere with SDK's PackageSelectorEnhancer +- ✅ **Automatic Sync**: Syncs selector from cart on page load +- ✅ **Flexible Config**: Easy quantity → package mapping +- ✅ **Multiple Selectors**: Support for multiple selectors on one page +- ✅ **Debug Logging**: Built-in debugging support +- ✅ **Zero Flicker**: Single cart update per action using `next.swapCart()` + +## Installation + +### Option 1: Inline Script + +```html + +``` + +### Option 2: Inline + +Copy the script content and paste it in a ` + + + + + + +``` + +## Notes + +- **Fully Standalone**: Uses custom `data-qty-*` attributes, zero conflict with SDK +- **No SDK Selector Enhancer**: No `data-next-cart-selector` needed +- **Display Attributes Work**: Keep `data-next-display` and `data-next-package-id` for SDK display system +- Requires SDK with `next.swapCart()` and `next.clearCart()` (for cart operations only) +- Handles its own quantity button click events +- Package swapping is atomic (single cart update via `swapCart`) +- Clears cart and resets to quantity 1 on page load +- No SDK modifications required diff --git a/docs/campaign-cart/examples/tier-selector-implementation.md b/docs/campaign-cart/examples/tier-selector-implementation.md new file mode 100644 index 0000000..64770d1 --- /dev/null +++ b/docs/campaign-cart/examples/tier-selector-implementation.md @@ -0,0 +1,450 @@ +# Tier Selector Implementation with Profiles + +This guide shows how to implement a pricing tier selector (Buy 1, Buy 2, Buy 3) using profiles combined with the variant system. + +## How It Works + +1. **Profiles** handle switching between pricing tiers (Buy 1 → Buy 2 → Buy 3) +2. **Variants** handle product options (Color, Size) +3. **Combined** they create a seamless tier + variant selection experience + +## Step 1: Configure Profiles for Each Tier + +```javascript +window.nextConfig = { + profiles: { + "buy_1": { + name: "Buy 1 - Regular Price", + description: "Single item purchase", + packageMappings: { + // Map default package IDs to Buy 1 tier packages + // Using a placeholder mapping approach + } + }, + "buy_2": { + name: "Buy 2 - Save 10%", + description: "Buy 2 and save", + packageMappings: { + // Chateau Ivory / Single + 19: 26, // Buy 1 (ref_id: 19) → Buy 2 (ref_id: 26) + // Obsidian Grey / Single + 17: 25, // Buy 1 (ref_id: 17) → Buy 2 (ref_id: 25) + // Add all other variant mappings... + } + }, + "buy_3": { + name: "Buy 3 - Save 20%", + description: "Best value - Buy 3", + packageMappings: { + // Map to Buy 3 tier packages + // (if they exist in your campaign) + } + } + }, + defaultProfile: "buy_1" // Start with Buy 1 tier +}; +``` + +## Step 2: Create Dynamic Profile Mappings + +Since you have many variants, generate the mappings dynamically: + +```javascript +// Helper function to build profile mappings dynamically +async function buildTierProfiles() { + const campaign = window.next.getCampaignData(); + if (!campaign) return; + + const profiles = { + buy_1: { name: "Buy 1", packageMappings: {} }, + buy_2: { name: "Buy 2", packageMappings: {} }, + buy_3: { name: "Buy 3", packageMappings: {} } + }; + + // Group packages by variant and tier + const variantGroups = {}; + + campaign.packages.forEach(pkg => { + // Create variant key from attributes + const variantKey = window.next.createVariantKey( + pkg.product_variant_attribute_values?.reduce((acc, attr) => { + acc[attr.code] = attr.value; + return acc; + }, {}) || {} + ); + + // Extract tier from package name + const tierMatch = pkg.name.match(/Buy (\d+)/i); + const tier = tierMatch ? `buy_${tierMatch[1]}` : 'buy_1'; + + // Store package by variant and tier + if (!variantGroups[variantKey]) { + variantGroups[variantKey] = {}; + } + variantGroups[variantKey][tier] = pkg.ref_id; + }); + + // Build mappings for each profile + Object.values(variantGroups).forEach(variant => { + if (variant.buy_1 && variant.buy_2) { + profiles.buy_2.packageMappings[variant.buy_1] = variant.buy_2; + } + if (variant.buy_1 && variant.buy_3) { + profiles.buy_3.packageMappings[variant.buy_1] = variant.buy_3; + } + }); + + // Register the profiles + Object.entries(profiles).forEach(([id, profile]) => { + window.next.registerProfile({ + id, + ...profile + }); + }); +} + +// Initialize on SDK ready +window.nextReady.push(buildTierProfiles); +``` + +## Step 3: HTML Implementation + +### Tier Selector UI + +```html + +
+

Select Quantity Tier:

+ + + + + + +
+ + + +``` + +### Variant Selector with Tier-Aware Pricing + +```html +
+ +
+ + + + + +
+ + +
+ +
+ Price: + $0.00 +
+ + +
+ Price (Buy 2): + $0.00 + Save 10%! +
+ + +
+ Price (Buy 3): + $0.00 + Save 20%! +
+
+ + +
+``` + +## Step 4: JavaScript Implementation + +```javascript +class TierVariantSelector { + constructor() { + this.productId = 3; // Grounded Sheets + this.selectedVariant = {}; + this.init(); + } + + async init() { + // Wait for SDK + await new Promise(resolve => window.nextReady.push(resolve)); + + // Build tier profiles + await this.buildTierProfiles(); + + // Set up event listeners + this.setupEventListeners(); + + // Initialize display + this.updateDisplay(); + } + + setupEventListeners() { + // Variant selectors + document.getElementById('color-select').addEventListener('change', (e) => { + this.selectedVariant.color = e.target.value; + this.updateDisplay(); + }); + + document.getElementById('size-select').addEventListener('change', (e) => { + this.selectedVariant.size = e.target.value; + this.updateDisplay(); + }); + + // Add to cart button + document.getElementById('add-to-cart').addEventListener('click', () => { + this.addToCart(); + }); + + // Listen for profile changes + window.next.on('profile:applied', () => { + this.updateDisplay(); + }); + } + + updateDisplay() { + // Check if variant is fully selected + if (!this.selectedVariant.color || !this.selectedVariant.size) { + document.getElementById('add-to-cart').disabled = true; + return; + } + + // Get current profile (tier) + const currentProfile = window.next.getActiveProfile() || 'buy_1'; + + // Get package for selected variant + const pkg = window.next.getPackageByVariantSelection( + this.productId, + this.selectedVariant + ); + + if (pkg) { + // The profile system will have already mapped to the correct tier package + // So pkg will be the correct package for the active tier + this.updatePriceDisplay(currentProfile, pkg.price); + document.getElementById('add-to-cart').disabled = false; + } + } + + updatePriceDisplay(tier, price) { + const priceElement = document.getElementById(`${tier.replace('_', '-')}-price`); + if (priceElement) { + priceElement.textContent = `$${price}`; + } + } + + async addToCart() { + const pkg = window.next.getPackageByVariantSelection( + this.productId, + this.selectedVariant + ); + + if (pkg) { + await window.next.addItem({ + packageId: pkg.ref_id, + quantity: 1 + }); + + // Show success message + this.showNotification('Added to cart!'); + } + } + + showNotification(message) { + // Implementation for showing notifications + console.log(message); + } + + async buildTierProfiles() { + // Use the implementation from Step 2 + // This builds profiles dynamically based on campaign data + } +} + +// Initialize on page load +document.addEventListener('DOMContentLoaded', () => { + new TierVariantSelector(); +}); +``` + +## Step 5: Advanced Implementation with Tier Comparison + +```html + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
QuantityPrice EachTotalSavings
Buy 1$0.00$0.00- + +
Buy 3$0.00$0.00Save 20% + +
+
+ + +``` + +## Key Benefits + +1. **Clean Separation**: Profiles handle tier switching, variants handle product options +2. **Automatic Mapping**: When user switches tiers, all cart items automatically update to new tier packages +3. **Session Persistence**: Selected tier persists across page refreshes +4. **URL Parameters**: Share tier-specific links: `?profile=buy_2` +5. **Easy Testing**: Switch between tiers without complex logic +6. **Cart Intelligence**: Items stay in cart when switching tiers + +## Best Practices + +1. **Default Tier**: Always set a default profile (usually "buy_1") +2. **Clear Naming**: Use consistent tier naming (buy_1, buy_2, etc.) +3. **Visual Feedback**: Show active tier clearly +4. **Price Comparison**: Display savings to encourage bulk purchases +5. **Mobile Friendly**: Ensure tier selector works on mobile + +## URL-Based Tier Selection + +Users can link directly to specific tiers: + +``` +https://yoursite.com/product?profile=buy_2 +https://yoursite.com/product?forceProfile=buy_3 // Clears cart first +``` + +## CSS Styling + +```css +.tier-selector { + display: flex; + gap: 1rem; + margin-bottom: 2rem; +} + +.tier-button { + flex: 1; + padding: 1rem; + border: 2px solid #ddd; + background: white; + cursor: pointer; + transition: all 0.3s; +} + +.tier-button:hover { + border-color: #007bff; +} + +.tier-button.next-profile-active { + background: #007bff; + color: white; + border-color: #007bff; +} + +.tier-comparison .recommended { + background: #f0f8ff; +} + +.tier-comparison .best-value { + background: #f0fff0; + font-weight: bold; +} +``` + +This approach leverages the profile system perfectly for tier-based pricing while maintaining clean, maintainable code. \ No newline at end of file diff --git a/docs/campaign-cart/examples/upsell-flow.md b/docs/campaign-cart/examples/upsell-flow.md new file mode 100644 index 0000000..4779514 --- /dev/null +++ b/docs/campaign-cart/examples/upsell-flow.md @@ -0,0 +1,532 @@ +# Complete Upsell Flow Example + +Multi-page upsell sequence implementation demonstrating different upsell patterns. + +## Page 1: First Upsell (Direct Offer) + +```html + + + + + + Special Offer - Extra Battery Pack + + + + + + + + + + + + + +
+ LIMITED TIME OFFER + +

Wait! Don't Fly Without Extra Power!

+ + Extra Battery Pack + +

Your Drone Pro X needs power to capture those amazing shots. Never miss a moment with our extra battery pack!

+ +
+ $119 + $79 +
+ +

+ Save $40 + (33% OFF) +

+ +
+ ⏰ Offer expires in: 05:00 +
+ +
    +
  • Extra 30 minutes flight time
  • +
  • Quick-swap design
  • +
  • LED charge indicator
  • +
  • 1-year warranty included
  • +
+ +
+ + +
+ +

+ This one-time offer won't be shown again +

+
+ + + + +``` + +## Page 2: Second Upsell (Selection Pattern) + +```html + + + + + + Protect Your Investment + + + + + + + + + + + + + +
+
+

Protect Your New Drone

+

Accidents happen. Don't let them ruin your investment.

+ +
+ +
+ $99 +

Basic Protection

+

1 Year Coverage

+
    +
  • Manufacturing defects
  • +
  • Technical support
  • +
  • Free shipping on repairs
  • +
+
+ + +
+ MOST POPULAR + $149 +

Premium Protection

+

2 Years Coverage

+
    +
  • Everything in Basic
  • +
  • Accidental damage coverage
  • +
  • One free replacement
  • +
  • Express replacement service
  • +
+
+ + +
+ $199 +

Ultimate Protection

+

3 Years Coverage

+
    +
  • Everything in Premium
  • +
  • Unlimited repairs
  • +
  • Annual maintenance
  • +
  • Priority support 24/7
  • +
+
+
+ +

+ Selected: Premium Protection +

+ +
+ + +
+
+
+ + +``` + +## Page 3: Final Upsell (Quantity Bundle) + +```html + + + + + + Stock Up on Accessories + + + + + + + + + + + + + +
+

Never Run Out of Propellers!

+

Professional pilots always keep extras. How many sets do you need?

+ + Propeller Sets + +
+
+

1 Set

+

$29

+

$29 per set

+
+ +
+ SAVE 20% +

3 Sets

+

$69

+

$23 per set

+
+ +
+ BEST VALUE +

5 Sets

+

$99

+

$19.80 per set

+
+
+ +

+ Total: $3 × + 23 = + $69 +

+ +
+ + +
+ +
+

Why Stock Up?

+
    +
  • Propellers wear out with regular use
  • +
  • Having spares means no downtime
  • +
  • Bulk pricing saves you money
  • +
  • Free shipping on all orders
  • +
+
+
+ + + + +``` + +## Receipt Page (Final) + +```html + + + + + + Order Confirmation + + + + + + + + + + + +
+
+ +

Thank You for Your Order!

+ +

+ Order #12345 has been confirmed +

+ +
+

Order Summary

+

Total: $1,295.00

+

Items: 5

+
+ +

A confirmation email has been sent to customer@email.com

+ +

Your order will be shipped to:

+
+ 123 Main St
+ Anytown, ST 12345 +
+
+ + +``` + +## Key Features Demonstrated + +1. **Multiple Upsell Types** - Direct, selection, and quantity patterns +2. **Progressive Flow** - Each page builds on previous purchases +3. **Urgency Elements** - Countdown timers and limited offers +4. **Value Communication** - Clear benefits and savings +5. **Mobile Responsive** - Works on all devices +6. **Easy Navigation** - Clear accept/decline options \ No newline at end of file diff --git a/docs/campaign-cart/getting-started/configuration.md b/docs/campaign-cart/getting-started/configuration.md new file mode 100644 index 0000000..89967a4 --- /dev/null +++ b/docs/campaign-cart/getting-started/configuration.md @@ -0,0 +1,60 @@ +# Configuration + +Configure the Next Commerce JS SDK using meta tags in your HTML head. + +## Configuration Options + +| Meta Tag | Description | Values | +|-----------|-------------|---------| +| `next-api-key` | Your campaign API key | String | +| `next-next-url` | URL to redirect after order completion | URL path | +| `next-debug` | Enable debug mode | `true`/`false` | +| `next-page-type` | Current page type | `product`, `checkout`, `upsell`, `receipt` | +| `next-prevent-back-navigation` | Prevent browser back button | `true`/`false` | + +## Basic Configuration + +```html + + +``` + +## Page Type Configuration + +Set the page type to enable page-specific features: + +```html + + + + + + + + + + + +``` + +## Upsell Configuration + +Configure upsell flow URLs: + +```html + + + +``` + +## Debug Mode + +Enable debug mode for development: + +```html + +``` + +## Environment-Specific Configuration + +TODO: Add information about environment-specific settings \ No newline at end of file diff --git a/docs/campaign-cart/getting-started/index.md b/docs/campaign-cart/getting-started/index.md new file mode 100644 index 0000000..70a1319 --- /dev/null +++ b/docs/campaign-cart/getting-started/index.md @@ -0,0 +1,25 @@ +--- +sidebar_label: Getting Started +sidebar_position: 1 +--- + +# Getting Started + +Get up and running with Campaign Cart JS SDK. Start here if you're new to the SDK. + +## Installation & Setup + +- [Installation](/docs/campaign-cart/getting-started/installation) - Setup and configuration +- [Configuration](/docs/campaign-cart/getting-started/configuration) - Meta tags and options +- [Quick Start](/docs/campaign-cart/getting-started/quick-start) - Basic examples to get started +- [URL Parameters](/docs/campaign-cart/getting-started/url-parameters) - forcePackageId and ref_id +- [Troubleshooting](/docs/campaign-cart/getting-started/troubleshooting) - Common issues and solutions + +## Next Steps + +Once you have the SDK installed and configured, explore: + +- [Cart System](/docs/campaign-cart/cart-system/overview) - Learn about cart management +- [Attributes](/docs/campaign-cart/attributes/overview) - Understand how to display dynamic content +- [Examples](/docs/campaign-cart/examples/basic-product-page) - See complete implementations + diff --git a/docs/campaign-cart/getting-started/installation.md b/docs/campaign-cart/getting-started/installation.md new file mode 100644 index 0000000..e0df7ff --- /dev/null +++ b/docs/campaign-cart/getting-started/installation.md @@ -0,0 +1,50 @@ +# Installation + +Add the Next Commerce JS SDK to your website with a simple script tag and configuration. + +## Basic Installation + +Add the SDK script and configuration to your HTML page: + +```html + + + + + + + + + + + + + + + + + +``` + +## Installation Order + +1. Add meta tags for configuration +2. Add the SDK script tag +3. SDK will auto-initialize when DOM is ready + +## Verifying Installation + +Check if the SDK is loaded: + +```javascript +// Wait for SDK to be fully initialized +window.addEventListener('next:initialized', function() { + console.log('SDK is ready'); +}); +``` + +## Next Steps + +- Configure your [meta tags](configuration.md) +- Follow the [quick start guide](quick-start.md) +- Learn about [cart selectors](../cart-system/selectors.md) \ No newline at end of file diff --git a/docs/campaign-cart/getting-started/quick-start.md b/docs/campaign-cart/getting-started/quick-start.md new file mode 100644 index 0000000..9379b16 --- /dev/null +++ b/docs/campaign-cart/getting-started/quick-start.md @@ -0,0 +1,110 @@ +# Quick Start Guide + +Get up and running with the Next Commerce JS SDK in 5 minutes. + +## Step 1: Add the SDK + +```html + + + + + + + + + + + + + +``` + +## Step 2: Add a Simple Product Selector + +```html + +
+
+

Basic Package

+ $99 +
+ +
+

Premium Package

+ $199 +
+
+``` + +## Step 3: Display Cart Information + +```html + +
+

Cart Summary

+

Items: 0

+

Total: $0.00

+
+ + +
+ Your cart is empty +
+``` + +## Step 4: Add Direct Add to Cart Button + +```html + + + + + +``` + +## Complete Example + +```html + + + + + + + +

Product Page

+ + +
+
+

1 Pack - $99

+
+
+

3 Pack - $199

+ Save 30% +
+
+ + +
+

Cart Total: $0.00

+
+ + +``` + +## Next Steps + +- Learn about [cart selectors](../cart-system/selectors.md) +- Add [toggle buttons](../cart-system/buttons.md) +- Implement [upsells](../upsells/overview.md) \ No newline at end of file diff --git a/docs/campaign-cart/getting-started/troubleshooting.md b/docs/campaign-cart/getting-started/troubleshooting.md new file mode 100644 index 0000000..e3d5fe1 --- /dev/null +++ b/docs/campaign-cart/getting-started/troubleshooting.md @@ -0,0 +1,61 @@ +# Troubleshooting + +Common issues and solutions when working with the Next Commerce JS SDK. + +## SDK Not Loading + +### Check API Key +Ensure your API key meta tag is present: +```html + +``` + +### Verify Script URL +Make sure the script URL is correct: +```html + +``` + +### Check Initialization +Listen for the initialized event: +```javascript +window.addEventListener('next:initialized', function() { + console.log('SDK is ready'); +}); +``` + +## Debug Mode + +Enable debug mode to see detailed logs: +```html + +``` + +## Common Issues + +### Attributes Not Working + +TODO: Add troubleshooting for attribute issues + +### Cart Not Updating + +TODO: Add troubleshooting for cart update issues + +### Upsells Not Showing + +TODO: Add troubleshooting for upsell issues + +### Events Not Firing + +TODO: Add troubleshooting for event issues + +## Browser Console + +Check the browser console for errors: +- Open DevTools (F12) +- Check Console tab for errors +- Look for "next:" prefixed messages + +## Getting Help + +TODO: Add support contact information \ No newline at end of file diff --git a/docs/campaign-cart/getting-started/url-parameters.md b/docs/campaign-cart/getting-started/url-parameters.md new file mode 100644 index 0000000..9c3342a --- /dev/null +++ b/docs/campaign-cart/getting-started/url-parameters.md @@ -0,0 +1,167 @@ +# URL Parameters + +The Next Commerce JS SDK supports several URL parameters that enable advanced functionality for testing, cart initialization, and order management. + +## Force Package ID + +The `forcePackageId` parameter allows you to pre-populate the cart with specific packages on page load. This is useful for: +- Direct marketing campaigns +- Testing specific cart configurations +- Creating quick-buy links +- Pre-filling carts from email campaigns + +### Usage + +``` +https://yoursite.com/checkout?forcePackageId=1 +``` + +### Format + +#### Single Package +``` +?forcePackageId=1 +``` +Adds package with ID 1 to the cart with quantity 1. + +#### Single Package with Quantity +``` +?forcePackageId=1:3 +``` +Adds package with ID 1 to the cart with quantity 3. + +#### Multiple Packages +``` +?forcePackageId=1:2,3:1,5:3 +``` +Adds: +- Package 1 with quantity 2 +- Package 3 with quantity 1 +- Package 5 with quantity 3 + +### Behavior + +1. **Cart Clearing**: When `forcePackageId` is present, the existing cart is cleared before adding the specified packages +2. **Validation**: Invalid package IDs are skipped with a warning logged +3. **Campaign Loading**: Packages are added after campaign data is loaded to ensure validity +4. **One-time Effect**: The parameter is processed once during SDK initialization + +### Examples + +#### Direct Product Link +Send customers directly to checkout with a specific product: +```html + + Buy Now - Special Offer + +``` + +#### Email Campaign with Multiple Items +Create a bundle link for email marketing: +```html + + Get the Complete Bundle + +``` + +#### Testing Different Quantities +Test checkout flow with various quantities: +``` +https://store.com/checkout?forcePackageId=1:10 +``` + +## Order Reference ID + +The `ref_id` parameter is used to load an existing order on upsell pages. This enables post-purchase upsell flows where customers can add additional items to their completed order. + +### Usage + +``` +https://yoursite.com/upsell?ref_id=ABC123 +``` + +### Behavior + +1. **Auto-loading**: When `ref_id` is present, the SDK automatically loads the order details +2. **Upsell Support**: The order must support post-purchase upsells (`supports_post_purchase_upsells: true`) +3. **Order Expiry**: Orders expire after 30 minutes by default +4. **State Persistence**: Order state is maintained across upsell pages + +### Use Cases + +#### Post-Purchase Upsell Flow +After a successful checkout, redirect to an upsell page: +```javascript +// On checkout success +window.location.href = `/upsell/special-offer?ref_id=${orderRefId}`; +``` + +#### Multi-Step Upsell Journey +The SDK automatically preserves `ref_id` across navigation: +```html + + + + +``` + +#### Thank You Page +Display order details on the confirmation page: +```html + +
Order #12345
+
$99.99
+``` + +## Combining Parameters + +You can combine multiple URL parameters: + +``` +https://yoursite.com/checkout?forcePackageId=1:2&debugger=true +``` + +This will: +1. Enable debug mode +2. Clear the cart and add package 1 with quantity 2 + +## Important Notes + +### Security Considerations +- `forcePackageId` bypasses normal cart selection UI - ensure packages are valid +- `ref_id` should be unique and non-guessable for security +- These parameters are intended for legitimate business use cases + +### Best Practices +1. **Validate Package IDs**: Ensure package IDs exist in your campaign before using in links +2. **Test Thoroughly**: Test forcePackageId links with various configurations +3. **Monitor Usage**: Track conversion rates for direct cart links +4. **Handle Errors**: Implement fallbacks if packages are unavailable + +### Limitations +- `forcePackageId` only works during initial page load +- Orders loaded via `ref_id` must not be expired +- Package availability is still subject to campaign rules + +## JavaScript API + +You can also achieve similar functionality programmatically: + +### Adding Items to Cart +```javascript +// Equivalent to ?forcePackageId=1:2 +window.next.addItem(1, 2); + +// Clear cart and add multiple items +window.next.clearCart(); +window.next.addItem(1, 2); +window.next.addItem(3, 1); +``` + +### Loading Orders +```javascript +// Equivalent to ?ref_id=ABC123 +window.next.loadOrder('ABC123'); +``` \ No newline at end of file diff --git a/docs/campaign-cart/guides/accessibility.md b/docs/campaign-cart/guides/accessibility.md new file mode 100644 index 0000000..021f272 --- /dev/null +++ b/docs/campaign-cart/guides/accessibility.md @@ -0,0 +1,369 @@ +# Accessibility Guide + +Making Next Commerce JS SDK implementations accessible to all users. + +## ARIA Attributes + +### Interactive Elements +```html + + + + + + + + +``` + +### Selectors +```html + +
+ +
+

Basic Package

+

1 drone, standard accessories

+
+ +
+

Premium Package

+

1 drone, extra batteries, case

+
+
+``` + +## Keyboard Navigation + +### Focus Management +```css +/* Visible focus indicators */ +:focus { + outline: 2px solid #007bff; + outline-offset: 2px; +} + +/* Don't remove outline without providing alternative */ +button:focus-visible { + outline: 3px solid #007bff; + outline-offset: 2px; +} + +/* Skip hidden elements */ +[data-next-hide="true"] { + display: none; /* Removes from tab order */ +} +``` + +### Keyboard Handlers +```javascript +// Handle keyboard navigation for selectors +document.querySelectorAll('[data-next-cart-selector]').forEach(selector => { + const cards = selector.querySelectorAll('[data-next-selector-card]'); + + cards.forEach((card, index) => { + card.addEventListener('keydown', (e) => { + let newIndex; + + switch(e.key) { + case 'ArrowDown': + case 'ArrowRight': + e.preventDefault(); + newIndex = (index + 1) % cards.length; + cards[newIndex].focus(); + cards[newIndex].click(); + break; + + case 'ArrowUp': + case 'ArrowLeft': + e.preventDefault(); + newIndex = (index - 1 + cards.length) % cards.length; + cards[newIndex].focus(); + cards[newIndex].click(); + break; + + case 'Enter': + case ' ': + e.preventDefault(); + card.click(); + break; + } + }); + }); +}); +``` + +## Screen Reader Support + +### Loading States +```html + +
+ Loading product information + ... +
+ + + +``` + +### Dynamic Updates +```html + +
+ + Cart updated. + 0 items, + total $0 + +
+ + +
+ + +``` + +## Form Accessibility + +### Labels and Instructions +```html + +
+
+ + + + Bulk quantities include automatic discount + +
+
+``` + +### Error Messages +```html + +
+ + + +
+ + +``` + +## Color and Contrast + +### Sufficient Contrast +```css +/* Ensure WCAG AA compliance (4.5:1 for normal text) */ +.price { + color: #333; /* On white background: 12.63:1 ✓ */ + background: white; +} + +.savings { + color: #1a7f1a; /* On white: 4.52:1 ✓ */ +} + +/* Don't rely on color alone */ +.out-of-stock { + color: #dc3545; + text-decoration: line-through; +} + +.out-of-stock::after { + content: " (Out of Stock)"; +} +``` + +## Responsive and Zoom + +### Scalable Text +```css +/* Use relative units */ +body { + font-size: 1rem; /* 16px default */ +} + +h1 { + font-size: 2rem; /* 32px */ +} + +/* Respect user preferences */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} +``` + +### Touch Targets +```css +/* Minimum 44x44px touch targets */ +button, +a, +[role="button"] { + min-height: 44px; + min-width: 44px; + padding: 12px; +} + +/* Spacing between targets */ +.button-group > * + * { + margin-left: 8px; +} +``` + +## Testing Accessibility + +### Keyboard Testing +1. Navigate using only Tab, Shift+Tab, Arrow keys, Enter, and Space +2. Ensure all interactive elements are reachable +3. Check focus indicators are visible +4. Verify expected keyboard behaviors work + +### Screen Reader Testing +1. Test with NVDA (Windows), JAWS, or VoiceOver (Mac) +2. Ensure all content is announced properly +3. Check dynamic updates are announced +4. Verify form labels and errors are clear + +### Automated Testing +```javascript +// Example using axe-core +import axe from 'axe-core'; + +async function runAccessibilityTests() { + const results = await axe.run(); + + if (results.violations.length) { + console.error('Accessibility violations:', results.violations); + } +} +``` + +## Checklist + +### General +- [ ] All images have appropriate alt text +- [ ] Page has proper heading hierarchy (h1 → h2 → h3) +- [ ] Links have descriptive text (not "click here") +- [ ] Page language is specified (``) + +### Interactive Elements +- [ ] All interactive elements are keyboard accessible +- [ ] Focus indicators are visible +- [ ] Buttons have accessible labels +- [ ] Form inputs have associated labels + +### Dynamic Content +- [ ] Loading states are announced +- [ ] Cart updates are announced +- [ ] Error messages are announced +- [ ] Success messages are announced + +### Visual Design +- [ ] Text has sufficient contrast (4.5:1 minimum) +- [ ] Information isn't conveyed by color alone +- [ ] Page is usable at 200% zoom +- [ ] Touch targets are at least 44x44px + +### Assistive Technology +- [ ] Page works with screen readers +- [ ] ARIA attributes are used correctly +- [ ] Semantic HTML is used appropriately +- [ ] Skip links are provided for navigation + +## Resources + +- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) +- [ARIA Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.1/) +- [WebAIM Resources](https://webaim.org/resources/) +- [A11y Project Checklist](https://www.a11yproject.com/checklist/) \ No newline at end of file diff --git a/docs/campaign-cart/guides/best-practices.md b/docs/campaign-cart/guides/best-practices.md new file mode 100644 index 0000000..e0dc654 --- /dev/null +++ b/docs/campaign-cart/guides/best-practices.md @@ -0,0 +1,319 @@ +# Best Practices + +Recommended patterns and anti-patterns for the Next Commerce JS SDK. + +## Performance Best Practices + +### Use Appropriate Selectors +```html + +
+ +
+ + +
+ +
+``` + +### Minimize Reflows +```html + +
+ Loading... +
+ + + +``` + +### Batch Operations +```javascript +// Good: Let SDK handle updates +next.on('cart:updated', (data) => { + // React to changes +}); + +// Avoid: Multiple individual updates +function updateEverything() { + updatePrice(); + updateQuantity(); + updateTotal(); + // Causes multiple reflows +} +``` + +## Code Organization + +### Separate Concerns +```html + +
+ $99 +
+ + +
+ +
+``` + +### Use Semantic HTML +```html + +
+

Product Name

+

Description

+ +
+ + +
+
+
+
+
+``` + +## Event Handling + +### Clean Up Listeners +```javascript +// Good: Remove listeners when done +const handler = (data) => console.log(data); +next.on('cart:updated', handler); + +// Later, when no longer needed +next.off('cart:updated', handler); + +// Avoid: Accumulating listeners +setInterval(() => { + next.on('cart:updated', (data) => { + // This creates a new listener every interval! + }); +}, 1000); +``` + +### Error Handling +```javascript +// Good: Handle errors gracefully +next.on('cart:item-added', (data) => { + try { + updateUI(data); + trackAnalytics(data); + } catch (error) { + console.error('Error handling cart update:', error); + // Show user-friendly message + } +}); + +// Avoid: Unhandled errors +next.on('cart:item-added', (data) => { + updateUI(data); // Could throw + trackAnalytics(data); // Could throw +}); +``` + +## State Management + +### Let SDK Manage State +```html + +
+ +
+ + + +``` + +### Avoid State Duplication +```javascript +// Good: Use SDK as single source of truth +function getCartTotal() { + return next.getCartData().total; +} + +// Avoid: Duplicating state +let myCartTotal = 0; +next.on('cart:updated', (data) => { + myCartTotal = data.total; // Unnecessary duplication +}); +``` + +## Accessibility + +### Provide Context +```html + + + + + +``` + +### Keyboard Navigation +```html + +
+ Package Option +
+ + +
+ Package Option +
+``` + +## Loading States + +### Progressive Enhancement +```html + +
+

Premium Package

+

Starting at $99

+
+ + +
+ + +
+``` + +## Mobile Optimization + +### Touch-Friendly Targets +```css +/* Good: Adequate touch targets */ +[data-next-selector-card] { + min-height: 44px; + padding: 12px; +} + +button[data-next-action] { + min-height: 44px; + min-width: 44px; +} + +/* Avoid: Too small for touch */ +button { + padding: 5px; + font-size: 12px; +} +``` + +### Responsive Layouts +```html + +
+ +
+``` + +## Security + +### Validate Input +```javascript +// Good: Validate before using +async function applyCouponCode(code) { + // Sanitize input + const sanitized = code.trim().toUpperCase().replace(/[^A-Z0-9]/g, ''); + + if (sanitized.length < 3 || sanitized.length > 20) { + alert('Invalid coupon code'); + return; + } + + const result = await next.applyCoupon(sanitized); + // Handle result +} + +// Avoid: Direct usage +async function applyCouponCode(code) { + const result = await next.applyCoupon(code); // No validation! +} +``` + +## Analytics Integration + +### Structured Tracking +```javascript +// Good: Consistent event structure +function trackEvent(action, category, label, value) { + if (window.gtag) { + gtag('event', action, { + event_category: category, + event_label: label, + value: value + }); + } +} + +next.on('cart:item-added', (data) => { + trackEvent('add_to_cart', 'ecommerce', data.item.name, data.item.price); +}); + +// Avoid: Inconsistent tracking +next.on('cart:item-added', (data) => { + gtag('event', 'cart_add'); // Missing data + fbq('track', 'AddToCart', { value: data.item.price }); // Different structure +}); +``` + +## Common Anti-Patterns to Avoid + +1. **Don't fight the SDK** - Work with its patterns, not against them +2. **Don't duplicate state** - Use SDK as single source of truth +3. **Don't bypass SDK methods** - Use provided APIs +4. **Don't over-optimize** - Let SDK handle performance +5. **Don't ignore errors** - Always handle edge cases +6. **Don't hardcode values** - Use dynamic data from SDK +7. **Don't break accessibility** - Maintain semantic HTML + +## Testing Strategies + +TODO: Add testing best practices + +## Debugging Tips + +Enable debug mode during development: +```html + +``` + +Use browser DevTools: +- Monitor Network tab for API calls +- Check Console for SDK messages +- Use Elements panel to inspect state classes \ No newline at end of file diff --git a/docs/campaign-cart/guides/index.md b/docs/campaign-cart/guides/index.md new file mode 100644 index 0000000..2dfbac3 --- /dev/null +++ b/docs/campaign-cart/guides/index.md @@ -0,0 +1,26 @@ +--- +sidebar_label: Guides +sidebar_position: 1 +--- + +# Guides + +Best practices, optimization tips, and advanced topics. + +## Best Practices + +- [Best Practices](/docs/campaign-cart/guides/best-practices) - Recommended patterns and approaches +- [Performance](/docs/campaign-cart/guides/performance) - Optimization tips +- [Accessibility](/docs/campaign-cart/guides/accessibility) - A11y considerations + +## Advanced Topics + +- [Profiles](/docs/campaign-cart/guides/profiles) - Dynamic package mapping system +- [Migration](/docs/campaign-cart/guides/migration) - Upgrading from older versions + +## Related + +- [Getting Started](/docs/campaign-cart/getting-started/installation) - Setup and configuration +- [API Reference](/docs/campaign-cart/api-reference/methods) - Complete API documentation +- [Examples](/docs/campaign-cart/examples/basic-product-page) - Implementation examples + diff --git a/docs/campaign-cart/guides/migration.md b/docs/campaign-cart/guides/migration.md new file mode 100644 index 0000000..21acaa9 --- /dev/null +++ b/docs/campaign-cart/guides/migration.md @@ -0,0 +1,45 @@ +# Migration Guide + +Guide for upgrading from older versions of the Next Commerce JS SDK. + +## Version Compatibility + +TODO: Add version compatibility matrix + +## Breaking Changes + +TODO: Add breaking changes by version + +## Migration Steps + +TODO: Add step-by-step migration instructions + +## Common Issues + +TODO: Add common migration issues and solutions + +## Deprecation Notices + +TODO: Add deprecated features and their replacements + +## Code Examples + +### Before (Old Version) + +TODO: Add old code examples + +### After (New Version) + +TODO: Add new code examples + +## Testing Your Migration + +TODO: Add testing strategies + +## Rollback Plan + +TODO: Add rollback instructions + +## Getting Help + +TODO: Add support resources for migration assistance \ No newline at end of file diff --git a/docs/campaign-cart/guides/performance.md b/docs/campaign-cart/guides/performance.md new file mode 100644 index 0000000..2028251 --- /dev/null +++ b/docs/campaign-cart/guides/performance.md @@ -0,0 +1,404 @@ +# Performance Optimization + +Tips and techniques for optimizing Next Commerce JS SDK performance. + +## Initial Load Optimization + +### Async Loading +```html + + + + + + +``` + +### Critical CSS +```html + + +``` + +### Resource Hints +```html + + + + + + + + + + +``` + +## Render Performance + +### Minimize Layout Thrashing +```javascript +// Bad: Causes multiple reflows +elements.forEach(el => { + el.style.height = el.offsetHeight + 10 + 'px'; // Read + el.style.width = el.offsetWidth + 10 + 'px'; // Read +}); + +// Good: Batch reads and writes +const measurements = elements.map(el => ({ + height: el.offsetHeight, + width: el.offsetWidth +})); + +elements.forEach((el, i) => { + el.style.height = measurements[i].height + 10 + 'px'; + el.style.width = measurements[i].width + 10 + 'px'; +}); +``` + +### Use CSS Transforms +```css +/* Bad: Triggers layout */ +.product-card:hover { + left: 10px; + top: -5px; +} + +/* Good: Uses GPU acceleration */ +.product-card:hover { + transform: translate(10px, -5px); +} +``` + +### Optimize Animations +```css +/* Enable GPU acceleration */ +.animated-element { + will-change: transform; + transform: translateZ(0); +} + +/* Clean up after animation */ +.animated-element.animation-done { + will-change: auto; +} +``` + +## Image Optimization + +### Lazy Loading +```html + +Product + + +
+ Product +
+``` + +### Responsive Images +```html + + + + Product + +``` + +## Large Data Sets + +### Virtual Scrolling +```html +
+
+ +
+ +
+
+
+``` + +### Pagination +```html + +
+ +
+ + + + +``` + +## Event Optimization + +### Debouncing +```javascript +// Debounce search input +function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; +} + +const searchProducts = debounce((query) => { + // Perform search +}, 300); + +document.querySelector('#search').addEventListener('input', (e) => { + searchProducts(e.target.value); +}); +``` + +### Throttling +```javascript +// Throttle scroll events +function throttle(func, limit) { + let inThrottle; + return function() { + const args = arguments; + const context = this; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +} + +const handleScroll = throttle(() => { + // Update UI based on scroll +}, 100); + +window.addEventListener('scroll', handleScroll); +``` + +### Event Delegation +```javascript +// Bad: Many listeners +document.querySelectorAll('.product-card').forEach(card => { + card.addEventListener('click', handleClick); +}); + +// Good: Single listener +document.querySelector('.product-grid').addEventListener('click', (e) => { + const card = e.target.closest('.product-card'); + if (card) { + handleClick(card); + } +}); +``` + +## Memory Management + +### Clean Up References +```javascript +// Remove event listeners +const controller = new AbortController(); + +next.on('cart:updated', handleUpdate, { signal: controller.signal }); + +// Later, clean up +controller.abort(); + +// Clear intervals/timeouts +const timers = new Set(); + +function createTimer(callback, delay) { + const id = setTimeout(() => { + callback(); + timers.delete(id); + }, delay); + timers.add(id); + return id; +} + +// Clean up all timers +function cleanup() { + timers.forEach(id => clearTimeout(id)); + timers.clear(); +} +``` + +### Avoid Memory Leaks +```javascript +// Bad: Closure keeps reference +function setupProduct(element) { + const hugeData = generateHugeDataSet(); + + element.addEventListener('click', () => { + // hugeData is kept in memory even if not used + console.log('clicked'); + }); +} + +// Good: Clean references +function setupProduct(element) { + let hugeData = generateHugeDataSet(); + + // Process data + processData(hugeData); + + // Clear reference + hugeData = null; + + element.addEventListener('click', () => { + console.log('clicked'); + }); +} +``` + +## Network Optimization + +### Batch API Calls +```javascript +// Bad: Multiple API calls +async function loadProductDetails(ids) { + const products = []; + for (const id of ids) { + const product = await fetchProduct(id); + products.push(product); + } + return products; +} + +// Good: Single batched call +async function loadProductDetails(ids) { + return await fetchProducts({ ids: ids }); +} +``` + +### Cache Responses +```javascript +// Simple cache implementation +const cache = new Map(); +const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes + +async function fetchWithCache(url) { + const cached = cache.get(url); + + if (cached && Date.now() - cached.timestamp < CACHE_DURATION) { + return cached.data; + } + + const data = await fetch(url).then(r => r.json()); + cache.set(url, { data, timestamp: Date.now() }); + + return data; +} +``` + +## Bundle Size + +### Tree Shaking +```javascript +// Bad: Import entire library +import * as utils from './utils'; + +// Good: Import only what you need +import { formatPrice, calculateTax } from './utils'; +``` + +### Code Splitting +```javascript +// Lazy load heavy features +button.addEventListener('click', async () => { + const { openAdvancedBuilder } = await import('./advanced-builder.js'); + openAdvancedBuilder(); +}); +``` + +## Monitoring Performance + +### Performance Observer +```javascript +// Monitor long tasks +const observer = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + console.warn('Long task detected:', entry.duration); + } +}); + +observer.observe({ entryTypes: ['longtask'] }); + +// Measure specific operations +performance.mark('cart-update-start'); +// ... operation ... +performance.mark('cart-update-end'); +performance.measure('cart-update', 'cart-update-start', 'cart-update-end'); +``` + +### Core Web Vitals +```javascript +// Monitor CLS (Cumulative Layout Shift) +let clsValue = 0; +const observer = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + if (!entry.hadRecentInput) { + clsValue += entry.value; + } + } +}); +observer.observe({ type: 'layout-shift', buffered: true }); +``` + +## Best Practices Summary + +1. **Load SDK asynchronously** - Don't block page render +2. **Use loading states** - Prevent layout shift +3. **Lazy load images** - Improve initial load +4. **Debounce events** - Reduce processing overhead +5. **Batch operations** - Minimize API calls +6. **Clean up resources** - Prevent memory leaks +7. **Monitor performance** - Track real user metrics + +## Performance Checklist + +- [ ] SDK loads asynchronously +- [ ] Critical CSS is inlined +- [ ] Images are optimized and lazy loaded +- [ ] Events are debounced/throttled appropriately +- [ ] Large lists use virtual scrolling or pagination +- [ ] Animations use CSS transforms +- [ ] Memory is properly managed +- [ ] API calls are batched when possible +- [ ] Performance metrics are monitored \ No newline at end of file diff --git a/docs/campaign-cart/guides/profiles.md b/docs/campaign-cart/guides/profiles.md new file mode 100644 index 0000000..865c548 --- /dev/null +++ b/docs/campaign-cart/guides/profiles.md @@ -0,0 +1,702 @@ +# Profile-Based Package Mapping System + +The Profile System enables dynamic package ID swapping across your entire e-commerce implementation. This powerful feature allows you to maintain multiple pricing tiers, seasonal promotions, and customer-specific offers without duplicating your frontend code. + +## Table of Contents + +- [Overview](#overview) +- [Core Concepts](#core-concepts) +- [Configuration](#configuration) +- [Implementation Methods](#implementation-methods) +- [JavaScript API](#javascript-api) +- [HTML Data Attributes](#html-data-attributes) +- [URL Parameters](#url-parameters) +- [Real-World Examples](#real-world-examples) +- [Advanced Usage](#advanced-usage) +- [Performance Considerations](#performance-considerations) +- [Troubleshooting](#troubleshooting) + +## Overview + +The Profile System solves a common e-commerce challenge: how to efficiently manage different product variants (regular price, discounted, bundles) without complex conditional logic throughout your codebase. Instead of hardcoding package IDs, you define mapping profiles that automatically swap packages based on the active profile. + +### Key Benefits + +- **Dynamic Pricing**: Switch between pricing tiers instantly +- **Seasonal Promotions**: Activate holiday sales with a single profile change +- **A/B Testing**: Test different pricing strategies seamlessly +- **Membership Tiers**: Offer VIP pricing to premium customers +- **Bundle Management**: Convert single items to multi-packs automatically + +## Core Concepts + +### What is a Profile? + +A profile is a named configuration that defines how package IDs should be mapped. When a profile is active, any reference to an original package ID is automatically translated to its mapped counterpart. + +```javascript +// Profile Definition +{ + id: "black_friday", + name: "Black Friday Sale", + packageMappings: { + 1: 101, // Package 1 → Package 101 (BF variant) + 2: 102, // Package 2 → Package 102 (BF variant) + // ... more mappings + } +} +``` + +### How Mapping Works + +1. **Original State**: Your site uses standard package IDs (1, 2, 3, etc.) +2. **Profile Activation**: User or system activates a profile +3. **Automatic Mapping**: All package operations use mapped IDs +4. **Transparent Integration**: Cart, checkout, and display components update automatically + +## Configuration + +### Basic Setup + +Add profile configuration to your `window.nextConfig`: + +```html + +``` + +### Grounded Sheets Example + +For the Grounded Sheets product line with multiple variants: + +```javascript +profiles: { + // Exit intent discount + "exit_10": { + name: "Exit 10% Discount", + packageMappings: { + // Single quantity mappings + 1: 78, // Twin Obsidian Grey → Exit 10% variant + 2: 79, // Twin Chateau Ivory → Exit 10% variant + 3: 82, // Double Obsidian Grey → Exit 10% variant + 4: 83, // Double Chateau Ivory → Exit 10% variant + 5: 86, // Queen Obsidian Grey → Exit 10% variant + 6: 90, // King Obsidian Grey → Exit 10% variant + 7: 87, // Queen Chateau Ivory → Exit 10% variant + 8: 91, // King Chateau Ivory → Exit 10% variant + // ... continue for all colors/sizes + } + }, + + // Bundle profiles + "2_pack": { + name: "2-Pack Bundle", + packageMappings: { + 1: 29, // Twin Obsidian Grey → 2-pack + 2: 30, // Twin Chateau Ivory → 2-pack + 3: 33, // Double Obsidian Grey → 2-pack + // ... all 2-pack mappings + } + }, + + "3_pack": { + name: "3-Pack Bundle", + packageMappings: { + 1: 53, // Twin Obsidian Grey → 3-pack + 2: 54, // Twin Chateau Ivory → 3-pack + 3: 58, // Double Obsidian Grey → 3-pack + // ... all 3-pack mappings + } + } +} +``` + +## Implementation Methods + +### Method 1: URL Parameters + +The simplest way to activate profiles is through URL parameters: + +``` +// Apply profile (preserves cart) +https://example.com/checkout?profile=black_friday + +// Force profile (clears cart first) +https://example.com/checkout?forceProfile=vip + +// Alternative parameter name +https://example.com/checkout?packageProfile=sale_20 +``` + +### Method 2: JavaScript API + +Programmatic control via the `window.next` API: + +```javascript +// Apply a profile +await window.next.setProfile('black_friday'); + +// Apply with options +await window.next.setProfile('vip', { + clearCart: false, // Keep existing cart items + preserveQuantities: true // Maintain item quantities +}); + +// Revert to no profile +await window.next.revertProfile(); + +// Check active profile +const currentProfile = window.next.getActiveProfile(); +console.log(currentProfile); // "black_friday" + +// Get mapped package ID +const mappedId = window.next.getMappedPackageId(1); +console.log(mappedId); // 101 (if black_friday is active) + +// Get original package ID from mapped +const originalId = window.next.getOriginalPackageId(101); +console.log(originalId); // 1 + +// List all available profiles +const profiles = window.next.listProfiles(); +console.log(profiles); // ["regular", "black_friday", "vip"] + +// Check if profile exists +if (window.next.hasProfile('black_friday')) { + // Profile is configured +} + +// Get profile information +const profileInfo = window.next.getProfileInfo('black_friday'); +console.log(profileInfo.name); // "Black Friday Sale" +``` + +### Method 3: HTML Data Attributes + +#### Profile Switcher Button + +```html + + + + + + + + + + + +``` + +#### Profile Selector Dropdown + +```html + + + + + +``` + +#### Profile-Aware Add to Cart + +```html + + +``` + +#### Conditional Display + +```html + +
+ 🎉 Black Friday prices are active! Save up to 40%! +
+ + +

Current Pricing: Regular

+

Profile ID: none

+``` + +## Real-World Examples + +### Example 1: Exit Intent Discount + +```javascript +// In your exit intent handler +window.next.exitIntent({ + image: '/images/special-offer.jpg', + action: async () => { + // Apply exit discount profile + await window.next.setProfile('exit_10'); + + // Optionally apply a coupon too + await window.next.applyCoupon('SAVE10'); + + // Show success message + alert('10% discount applied to your cart!'); + } +}); +``` + +### Example 2: Membership Tier System + +```javascript +// After user login +async function applyMembershipPricing(user) { + if (user.membership === 'vip_gold') { + await window.next.setProfile('vip_gold'); + } else if (user.membership === 'vip_silver') { + await window.next.setProfile('vip_silver'); + } + // Regular members use default pricing +} + +// Check if user qualifies for special pricing +document.addEventListener('DOMContentLoaded', async () => { + const user = await fetchUserData(); + if (user && user.membership) { + applyMembershipPricing(user); + } +}); +``` + +### Example 3: Time-Based Promotions + +```javascript +// Automatic profile activation based on time +function checkAndApplyPromotions() { + const now = new Date(); + const hour = now.getHours(); + + // Happy hour: 3-6 PM + if (hour >= 15 && hour < 18) { + window.next.setProfile('happy_hour'); + } + // Flash sale: Midnight to 2 AM + else if (hour >= 0 && hour < 2) { + window.next.setProfile('flash_sale'); + } + // Weekend special + else if (now.getDay() === 0 || now.getDay() === 6) { + window.next.setProfile('weekend_special'); + } +} + +// Check every hour +setInterval(checkAndApplyPromotions, 3600000); +checkAndApplyPromotions(); // Initial check +``` + +### Example 4: Quantity-Based Bundle Conversion + +```html + +
+

Select Bundle Size:

+ + + + + + +
+ + +``` + +### Example 5: A/B Testing + +```javascript +// A/B test different pricing strategies +function initPricingExperiment() { + // Get or generate user variant + let variant = localStorage.getItem('pricing_variant'); + + if (!variant) { + // Randomly assign variant + variant = Math.random() < 0.5 ? 'control' : 'test'; + localStorage.setItem('pricing_variant', variant); + } + + // Apply corresponding profile + if (variant === 'test') { + window.next.setProfile('test_pricing'); + + // Track with analytics + window.next.trackCustomEvent('experiment_variant', { + experiment: 'pricing_test', + variant: 'test' + }); + } +} + +initPricingExperiment(); +``` + +## Advanced Usage + +### Profile Events + +Listen to profile change events: + +```javascript +// Listen for profile changes +window.next.on('profile:applied', (data) => { + console.log(`Profile changed to: ${data.profileId}`); + console.log(`Items swapped: ${data.itemsSwapped}`); + + // Update UI elements + updatePricingBadges(); + showProfileNotification(data.profileId); +}); + +// Listen for profile revert +window.next.on('profile:reverted', (data) => { + console.log(`Profile reverted, restored ${data.itemsRestored} items`); +}); +``` + +### Custom Profile Registration + +Register profiles dynamically at runtime: + +```javascript +// Register a new profile programmatically +window.next.registerProfile({ + id: 'custom_sale', + name: 'Custom Sale', + description: 'Dynamically created sale', + packageMappings: { + 1: 501, + 2: 502, + 3: 503 + } +}); + +// Now you can use it +await window.next.setProfile('custom_sale'); +``` + +### Profile Validation + +Check if mappings are valid before applying: + +```javascript +async function validateAndApplyProfile(profileId) { + const profile = window.next.getProfileInfo(profileId); + + if (!profile) { + console.error('Profile not found'); + return false; + } + + // Check if all mapped packages exist in campaign + const campaign = window.next.getCampaignData(); + const invalidMappings = []; + + for (const [original, mapped] of Object.entries(profile.packageMappings)) { + const packageExists = campaign.packages.some(p => p.ref_id === mapped); + if (!packageExists) { + invalidMappings.push({ original, mapped }); + } + } + + if (invalidMappings.length > 0) { + console.warn('Invalid mappings found:', invalidMappings); + // Decide whether to proceed + } + + await window.next.setProfile(profileId); + return true; +} +``` + +### Combining with Coupons + +Apply profiles and coupons together: + +```javascript +async function applySpecialOffer(offerCode) { + switch (offerCode) { + case 'BLACKFRIDAY': + await window.next.setProfile('black_friday'); + await window.next.applyCoupon('BF2024'); + break; + + case 'VIP_WELCOME': + await window.next.setProfile('vip'); + await window.next.applyCoupon('WELCOME10'); + break; + + case 'BUNDLE_DEAL': + await window.next.setProfile('3_pack'); + // No coupon needed, profile handles discount + break; + } +} +``` + +## Performance Considerations + +### Mapping Efficiency + +- **O(1) Lookups**: Profile mappings use hash maps for instant lookups +- **Reverse Mappings**: Automatically generated for bidirectional lookups +- **Caching**: Active profile cached in session storage +- **Lazy Loading**: Profile system only loads when needed + +### Best Practices + +1. **Predefine Profiles**: Configure all profiles at initialization +2. **Minimize Switches**: Avoid frequent profile changes +3. **Batch Operations**: Apply profiles before adding multiple items +4. **Use Default Profile**: Set a default to avoid null checks + +### Memory Management + +```javascript +// Profile data structure is optimized +{ + profiles: Map, // Efficient storage + activeProfileId: string | null, // Single reference + reverseMapping: Map, // Pre-computed + originalCartSnapshot: CartItem[] // Only when needed +} +``` + +## Troubleshooting + +### Common Issues + +#### Profile Not Applying + +```javascript +// Debug profile application +console.log('Active profile:', window.next.getActiveProfile()); +console.log('Available profiles:', window.next.listProfiles()); + +// Check if profile exists +if (!window.next.hasProfile('my_profile')) { + console.error('Profile not configured'); +} + +// Verify mappings +const profile = window.next.getProfileInfo('my_profile'); +console.log('Mappings:', profile.packageMappings); +``` + +#### Cart Items Not Updating + +```javascript +// Force cart recalculation after profile change +window.next.on('profile:applied', async () => { + // Trigger cart UI update + const cartStore = window.next.stores.cart.getState(); + await cartStore.calculateTotals(); + + // Emit update event + window.dispatchEvent(new CustomEvent('cart:updated')); +}); +``` + +#### Package Not Found After Mapping + +```javascript +// Validate mapped packages exist +async function checkMappedPackages(profileId) { + const profile = window.next.getProfileInfo(profileId); + const campaign = window.next.getCampaignData(); + + for (const [original, mapped] of Object.entries(profile.packageMappings)) { + const pkg = campaign.packages.find(p => p.ref_id === mapped); + if (!pkg) { + console.error(`Mapped package ${mapped} not found for original ${original}`); + } + } +} +``` + +### Debug Mode + +Enable debug mode to see profile operations: + +```javascript +// Enable debug mode +window.nextConfig.debug = true; + +// Or via URL +?debugger=true&profile=black_friday + +// Access debug utilities +window.nextDebug.attribution.debug(); +window.nextDebug.getStats(); +``` + +### Testing Profiles + +```javascript +// Test profile switching +async function testProfiles() { + const testCart = [ + { packageId: 1, quantity: 2 }, + { packageId: 2, quantity: 1 } + ]; + + // Clear and set up test cart + await window.next.clearCart(); + for (const item of testCart) { + await window.next.addItem(item); + } + + console.log('Original cart:', window.next.getCartData()); + + // Test each profile + for (const profileId of window.next.listProfiles()) { + await window.next.setProfile(profileId); + console.log(`Profile ${profileId}:`, window.next.getCartData()); + } + + // Revert to original + await window.next.revertProfile(); + console.log('Reverted cart:', window.next.getCartData()); +} +``` + +## Migration Guide + +### From Hardcoded Package IDs + +Before: +```javascript +// Old approach - hardcoded variants +if (isBlackFriday) { + addToCart(101); // Black Friday variant +} else { + addToCart(1); // Regular variant +} +``` + +After: +```javascript +// New approach - profile-based +if (isBlackFriday) { + await window.next.setProfile('black_friday'); +} +addToCart(1); // Always use base ID, profile handles mapping +``` + +### From Multiple Product Pages + +Before: +```html + + + + + +``` + +After: +```html + + + +``` + +## Summary + +The Profile System provides a powerful, flexible way to manage package variations without code duplication. By defining simple mappings, you can instantly switch between different pricing strategies, seasonal promotions, and customer tiers while maintaining a clean, maintainable codebase. + +Key takeaways: +- **Define once, use everywhere**: Profiles work across all SDK components +- **Zero frontend changes**: Existing package IDs automatically map +- **Performance optimized**: O(1) lookups with intelligent caching +- **Fully integrated**: Works with cart, checkout, analytics, and display systems +- **Developer friendly**: Simple API with comprehensive debugging tools \ No newline at end of file diff --git a/docs/campaign-cart/index.md b/docs/campaign-cart/index.md new file mode 100644 index 0000000..7d68274 --- /dev/null +++ b/docs/campaign-cart/index.md @@ -0,0 +1,85 @@ +--- +sidebar_label: Campaign Cart +sidebar_position: 1 +--- + +# Campaign Cart JS SDK + +Welcome to the Next Commerce JS SDK documentation. This SDK enables developers to create e-commerce storefront experiences using HTML attributes, JavaScript, and CSS. + +## Quick Start + +```html + + + + + +``` + +## Features + +- **Attribute-driven architecture** - Build cart functionality with HTML attributes +- **Cart management** - Add to cart, selectors, quantity controls +- **Profile-based pricing** - Dynamic package mapping for different pricing tiers +- **Post-purchase upsells** - Maximize order value with upsell flows +- **Dynamic content** - Display prices, totals, and product data +- **Conversion tools** - FOMO notifications and exit intent popups + +## Documentation Sections + +### Getting Started +Start here if you're new to Campaign Cart. Learn how to install, configure, and get your first cart working. + +- [Installation](/docs/campaign-cart/getting-started/installation) - Setup and configuration +- [Quick Start](/docs/campaign-cart/getting-started/quick-start) - Basic examples +- [Configuration](/docs/campaign-cart/getting-started/configuration) - Meta tags and options +- [Troubleshooting](/docs/campaign-cart/getting-started/troubleshooting) - Common issues + +### Core Features + +- **[Cart System](/docs/campaign-cart/cart-system/overview)** - Cart management and controls +- **[Upsells](/docs/campaign-cart/upsells/overview)** - Post-purchase upsell flows +- **[Attributes](/docs/campaign-cart/attributes/overview)** - Display and conditional attributes +- **[Utilities](/docs/campaign-cart/utilities/analytics)** - FOMO, exit intent, and analytics + +### Reference + +- **[API Reference](/docs/campaign-cart/api-reference/methods)** - JavaScript methods and events +- **[Examples](/docs/campaign-cart/examples/basic-product-page)** - Complete implementations +- **[Guides](/docs/campaign-cart/guides/best-practices)** - Best practices and optimization + +## Quick Examples + +### Add to Cart Button +```html + +``` + +### Product Selector +```html +
+
Option 1
+
Option 2
+
+``` + +### Display Cart Total +```html +$0.00 +``` + +### Conditional Display +```html +
+ +
+``` + +## Browser Support + +The SDK supports all modern browsers including Chrome, Firefox, Safari, and Edge. diff --git a/docs/campaign-cart/multi-currency/flow-chart.md b/docs/campaign-cart/multi-currency/flow-chart.md new file mode 100644 index 0000000..7617c94 --- /dev/null +++ b/docs/campaign-cart/multi-currency/flow-chart.md @@ -0,0 +1,189 @@ +# Multi-Currency System Flow Chart + +## System Architecture Flow + +```mermaid +graph TB + Start([Page Load]) --> Init[index.ts: Auto-initialize SDK] + Init --> SDK[SDKInitializer.initialize] + + SDK --> InitLoc[initializeLocationAndCurrency] + + InitLoc --> DetectCountry{Detect Country} + DetectCountry -->|URL Param ?country=XX| URLCountry[Use URL Country] + DetectCountry -->|Session Storage| SavedCountry[Use Saved Country] + DetectCountry -->|CDN API| APICountry[Fetch from CDN Workers] + + URLCountry --> DetermineCurrency + SavedCountry --> DetermineCurrency + APICountry --> DetermineCurrency + + DetermineCurrency{Determine Currency} + DetermineCurrency -->|URL Param ?currency=XXX| URLCurrency[Use URL Currency
Save to Session] + DetermineCurrency -->|Session Storage| SessionCurrency[Use Saved Currency] + DetermineCurrency -->|Country Config| CountryCurrency[Use Country's Currency] + + URLCurrency --> StoreConfig + SessionCurrency --> StoreConfig + CountryCurrency --> StoreConfig + + StoreConfig[Store in ConfigStore] + StoreConfig --> LoadCampaign[Load Campaign with Currency] + + LoadCampaign --> CampaignCache{Check Campaign Cache} + CampaignCache -->|Cache Hit
< 10 min| UseCached[Use Cached Data] + CampaignCache -->|Cache Miss| FetchCampaign[Fetch from API
with Currency] + + UseCached --> InitEnhancers + FetchCampaign --> CacheData[Cache per Currency
next-campaign-cache_CURRENCY] + CacheData --> InitEnhancers + + InitEnhancers[Initialize Enhancers] + InitEnhancers --> NextCore[NextCommerce.init] + NextCore --> EnhancerLoop[Load & Apply Enhancers] + + EnhancerLoop --> CheckoutForm[CheckoutFormEnhancer] + EnhancerLoop --> CartDisplay[CartDisplayEnhancer] + EnhancerLoop --> OtherEnhancers[Other Enhancers...] +``` + +## Currency Change Flow (Checkout) + +```mermaid +graph TB + UserAction([User Changes Country
in Checkout]) --> FormChange[CheckoutFormEnhancer
handleFieldChange] + + FormChange --> IsShipping{Is Shipping Country?} + IsShipping -->|Yes| CheckCurrency[handleCountryCurrencyChange] + IsShipping -->|No - Billing| UpdateStates[Update State Options Only] + + CheckCurrency --> Dedupe{Check Deduplication
< 500ms?} + Dedupe -->|Duplicate| Skip[Skip Processing] + Dedupe -->|New Request| GetCountryData[Fetch Country Config] + + GetCountryData --> CompareCurrency{Different Currency?} + CompareCurrency -->|No| UpdateStates + CompareCurrency -->|Yes| SwitchCurrency[Switch Currency] + + SwitchCurrency --> SaveSession[Save to Session Storage] + SaveSession --> ReloadCampaign[Reload Campaign
with New Currency] + + ReloadCampaign --> UpdateCart[cartStore.refreshItemPrices] + UpdateCart --> UpdatePrices[Update All Prices] + + UpdatePrices --> PackagePrices[Update Package Prices] + UpdatePrices --> ShippingPrices[Update Shipping Prices] + UpdatePrices --> RecalcTotals[Recalculate Totals] + + RecalcTotals --> UpdateDisplay[Update Display] + UpdateDisplay --> FormatCurrency[Format with Currency Symbol] + + UpdateStates --> LoadStates{Check State Cache} + LoadStates -->|Promise Exists| WaitPromise[Await Existing Promise] + LoadStates -->|No Promise| CreatePromise[Create & Store Promise] + + WaitPromise --> PopulateStates + CreatePromise --> FetchStates[Fetch from CDN API] + FetchStates --> CacheStates[Cache States Data] + CacheStates --> PopulateStates[Populate State Dropdown] +``` + +## Currency Detection Priority + +```mermaid +graph LR + Priority[Currency Priority] --> P1[1. URL Parameter
?currency=XXX] + P1 --> P2[2. Session Storage
next_selected_currency] + P2 --> P3[3. Country Config
From CDN API] + P3 --> P4[4. Fallback
USD] + + style P1 fill:#4CAF50,color:#fff + style P2 fill:#2196F3,color:#fff + style P3 fill:#FF9800,color:#fff + style P4 fill:#9E9E9E,color:#fff +``` + +## Data Flow & Caching + +```mermaid +graph TB + subgraph "Location Data" + LocAPI[CDN Workers API
location endpoint] + LocCache[localStorage Cache
1 hour TTL] + LocAPI -.->|Cache| LocCache + end + + subgraph "Country Config" + CountryAPI[CDN Workers API
countries/{code}/states] + StateCache[sessionStorage Cache
per country, 1 hour TTL] + CountryAPI -.->|Cache| StateCache + end + + subgraph "Campaign Data" + CampaignAPI[NextCommerce API
campaigns/?currency={code}] + CampaignCache[sessionStorage Cache
per currency, 10 min TTL] + CampaignAPI -.->|Cache| CampaignCache + end + + subgraph "Currency State" + ConfigStore[ConfigStore
Runtime State] + SessionStore[sessionStorage
next_selected_currency] + ConfigStore <-.->|Sync| SessionStore + end + + LocCache --> ConfigStore + StateCache --> ConfigStore + CampaignCache --> ConfigStore +``` + +## Component Interactions + +```mermaid +sequenceDiagram + participant User + participant CheckoutForm + participant ConfigStore + participant CampaignStore + participant CartStore + participant API + + User->>CheckoutForm: Change Country (Shipping) + CheckoutForm->>CheckoutForm: Check Deduplication + CheckoutForm->>API: Get Country Config + API-->>CheckoutForm: Country Data + Currency + + alt Different Currency + CheckoutForm->>ConfigStore: Update Currency + ConfigStore->>CampaignStore: Trigger Reload + CampaignStore->>API: Fetch Campaign (new currency) + API-->>CampaignStore: Campaign Data + CampaignStore->>CartStore: Notify Update + CartStore->>CartStore: refreshItemPrices() + CartStore->>CheckoutForm: Prices Updated + end + + CheckoutForm->>User: Display Updated +``` + +## Error Handling & Fallbacks + +```mermaid +graph TB + Error([API Error/Timeout]) --> Check1{Location API Failed?} + Check1 -->|Yes| FallbackLoc[Use US as default] + Check1 -->|No| Check2{Country Config Failed?} + + Check2 -->|Yes| FallbackCountry[Use cached or default] + Check2 -->|No| Check3{Campaign API Failed?} + + Check3 -->|Yes| FallbackCampaign[Use cached campaign] + Check3 -->|No| Success[Continue Normal Flow] + + FallbackLoc --> DefaultCurrency[Default to USD] + FallbackCountry --> DefaultCurrency + FallbackCampaign --> UseCached[Use last known prices] + + style Error fill:#f44336,color:#fff + style DefaultCurrency fill:#FF9800,color:#fff + style UseCached fill:#2196F3,color:#fff +``` \ No newline at end of file diff --git a/docs/campaign-cart/multi-currency/index.md b/docs/campaign-cart/multi-currency/index.md new file mode 100644 index 0000000..bb39b5b --- /dev/null +++ b/docs/campaign-cart/multi-currency/index.md @@ -0,0 +1,17 @@ +--- +sidebar_label: Multi-Currency +sidebar_position: 1 +--- + +# Multi-Currency + +Multi-currency support and configuration. + +- [Overview](/docs/campaign-cart/multi-currency/overview) - Multi-currency introduction +- [Flow Chart](/docs/campaign-cart/multi-currency/flow-chart) - Currency selection flow + +## Related + +- [Getting Started - Configuration](/docs/campaign-cart/getting-started/configuration) - Basic configuration +- [Attributes - Formatting](/docs/campaign-cart/attributes/formatting) - Value formatting + diff --git a/docs/campaign-cart/multi-currency/overview.md b/docs/campaign-cart/multi-currency/overview.md new file mode 100644 index 0000000..0194473 --- /dev/null +++ b/docs/campaign-cart/multi-currency/overview.md @@ -0,0 +1,324 @@ +# Multi-Currency System Overview + +The Campaign Cart SDK provides a sophisticated multi-currency system that automatically detects user location, determines appropriate currency, and seamlessly updates pricing across all components. + +## Table of Contents +- [Architecture](#architecture) +- [Currency Detection Flow](#currency-detection-flow) +- [Key Components](#key-components) +- [Configuration](#configuration) +- [Currency Switching](#currency-switching) +- [Price Updates](#price-updates) +- [Display Formatting](#display-formatting) + +## Architecture + +The multi-currency system is built on several interconnected components: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ User's Browser │ +├─────────────────────────────────────────────────────────────┤ +│ 1. Location Detection (CDN API) │ +│ 2. Currency Determination (Priority System) │ +│ 3. Campaign Loading (with Currency) │ +│ 4. Price Updates & Display │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Currency Detection Flow + +### 1. Initial Detection (SDKInitializer) + +When the SDK initializes, it follows this priority order to determine currency: + +1. **URL Parameter** (`?currency=XXX`) - Highest priority +2. **Session Storage** (`next_selected_currency`) - User's previous selection +3. **Country Detection** - Based on user's location/IP + +```javascript +// Priority logic in SDKInitializer.ts +if (urlCurrency) { + selectedCurrency = urlCurrency.toUpperCase(); + sessionStorage.setItem('next_selected_currency', selectedCurrency); +} else if (savedCurrency) { + selectedCurrency = savedCurrency; +} else { + selectedCurrency = detectedCurrency; +} +``` + +### 2. Country Detection Sources + +The system determines the user's country through: + +1. **URL Parameter** (`?country=XX`) - Force specific country +2. **Session Storage** (`next_selected_country`) - Saved preference +3. **IP-based Detection** - Via CDN Workers API + +### 3. API Endpoints + +- **Location Detection**: `https://cdn-countries.muddy-wind-c7ca.workers.dev/location` +- **Country Config**: `https://cdn-countries.muddy-wind-c7ca.workers.dev/countries/{COUNTRY_CODE}/states` +- **Campaign Data**: `https://campaigns.apps.29next.com/api/v1/campaigns/?currency={CURRENCY_CODE}` + +## Key Components + +### SDKInitializer (`src/enhancers/core/SDKInitializer.ts`) + +Handles the initial currency detection and setup: + +```typescript +private static async initializeLocationAndCurrency(): Promise { + // 1. Check for overrides (URL, session) + // 2. Fetch location data + // 3. Determine currency + // 4. Store in ConfigStore +} +``` + +### CampaignStore (`src/stores/campaignStore.ts`) + +Manages campaign data with per-currency caching: + +```typescript +// Separate cache for each currency +const cacheKey = `next-campaign-cache_${currency}`; +// 10-minute TTL per currency +``` + +### CartStore (`src/stores/cartStore.ts`) + +Updates prices when currency changes: + +```typescript +refreshItemPrices: async () => { + // Update package prices + // Update shipping method prices + // Recalculate totals +} +``` + +### CheckoutFormEnhancer (`src/enhancers/checkout/CheckoutFormEnhancer.ts`) + +Handles country-based currency switching in checkout: + +```typescript +private async handleCountryCurrencyChange(countryCode: string): Promise { + // Check if country has different currency + // Reload campaign if needed + // Update cart prices +} +``` + +## Configuration + +### Window Configuration + +```javascript +window.nextConfig = { + apiKey: 'your-api-key', + debug: false, + + // Currency behavior + currencyBehavior: 'auto', // 'auto' | 'manual' + + // Address configuration + addressConfig: { + defaultCountry: 'US', + showCountries: ['US', 'CA', 'GB', 'AU'], + dontShowStates: ['AS', 'GU', 'PR'] + }, + + // Google Maps for address autocomplete + googleMapsConfig: { + apiKey: 'your-google-maps-key', + enableAutocomplete: true, + region: 'US' + } +}; +``` + +### Meta Tag Configuration + +```html + + + + + + + + + + +``` + +## Currency Switching + +### User-Initiated Currency Change + +Currency can be changed through: + +1. **Country Selection in Checkout** - Automatically switches to country's currency +2. **Debug Currency Selector** - Manual currency selection (debug mode) +3. **URL Parameter** - Direct currency override + +### Automatic Currency Switching + +When a user changes their shipping country in checkout: + +```typescript +// CheckoutFormEnhancer.ts +if (fieldName === 'country') { + // Update state options + await this.updateStateOptions(target.value, provinceField); + + // Auto-switch currency if different + await this.handleCountryCurrencyChange(target.value, target); +} +``` + +### Deduplication + +To prevent duplicate API calls when country changes trigger multiple events: + +```typescript +// Deduplication with 500ms window +if (this.lastCurrencyChangeCountry === countryCode && + (now - this.lastCurrencyChangeTime) < 500) { + return; // Skip duplicate +} +``` + +## Price Updates + +### When Currency Changes + +1. **Campaign Reload** - Fetches new prices from API +2. **Cart Update** - Updates all item prices +3. **Shipping Update** - Updates shipping method prices +4. **Display Refresh** - Updates all displayed prices + +### Update Flow + +```typescript +// cartStore.refreshItemPrices() +1. Get new campaign data with currency +2. Update each cart item with new prices +3. Update shipping method price +4. Recalculate totals +5. Update enriched items for display +``` + +## Display Formatting + +### Currency Symbol Resolution + +The system determines currency symbols through multiple sources: + +```typescript +// Priority order in CartDisplayEnhancer +1. locationData.detectedCountryConfig.currencySymbol +2. campaign.currency (from API) +3. Intl.NumberFormat fallback +``` + +### Formatting Functions + +```typescript +// Centralized formatting +formatCurrency(amount: number, currency: string): string { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency, + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(amount); +} +``` + +## Caching Strategy + +### Campaign Data Caching + +- **Per-Currency Caching**: Each currency has its own cache entry +- **Cache Key**: `next-campaign-cache_${currency}` +- **TTL**: 10 minutes +- **Storage**: Session Storage + +### Country Data Caching + +- **Location Data**: Cached for 1 hour in localStorage +- **State Data**: Cached per country for 1 hour +- **Cache Key**: `next_country_states_${countryCode}` + +## Error Handling + +### Fallback Behavior + +If currency detection fails: + +1. Check for saved currency in session +2. Check for URL parameter +3. Default to USD + +### Network Failures + +- Location API timeout: 3 seconds +- Fallback to US/USD if detection fails +- Campaign data uses cached version if available + +## Testing + +### URL Parameters for Testing + +``` +# Force specific country +?country=GB + +# Force specific currency +?currency=EUR + +# Force both +?country=AU¤cy=AUD + +# Enable debug mode +?debugger=true +``` + +### Debug Mode Features + +When debug mode is enabled: +- Currency selector in debug overlay +- Country selector for testing +- Detailed logging of currency changes +- Cache inspection tools + +## Best Practices + +1. **Always Handle Currency Changes**: Components should subscribe to currency changes +2. **Use Centralized Formatting**: Use the formatCurrency utility +3. **Cache Appropriately**: Respect TTLs for performance +4. **Test Multiple Currencies**: Verify pricing in all supported currencies +5. **Handle Edge Cases**: Account for currency switching during checkout + +## Troubleshooting + +### Common Issues + +1. **Duplicate API Calls** + - Check for multiple event listeners + - Verify deduplication is working + +2. **Wrong Currency Display** + - Check priority order (URL > Session > Detection) + - Verify campaign supports the currency + +3. **Prices Not Updating** + - Ensure refreshItemPrices is called + - Check if shipping methods are updated + +4. **Currency Symbol Issues** + - Verify location data includes currencySymbol + - Check Intl.NumberFormat support \ No newline at end of file diff --git a/docs/campaign-cart/upsells/configuration.md b/docs/campaign-cart/upsells/configuration.md new file mode 100644 index 0000000..3127d8e --- /dev/null +++ b/docs/campaign-cart/upsells/configuration.md @@ -0,0 +1,92 @@ +# Upsell Configuration + +Configure upsell flows and behavior with meta tags and attributes. + +## Basic Configuration + +Set URLs for upsell flow navigation: + +```html + + + + + +``` + +## Page Configuration + +Mark pages as upsell pages: + +```html + + + + + +``` + +## Order Context + +Upsells need order context (from URL parameter): + +```html + + +``` + +## Multiple Upsell Pages + +Chain multiple upsell pages: + +```html + + + + + + + +``` + +## Conditional Routing + +TODO: Add information about: +- Conditional upsell routing based on cart contents +- A/B testing different upsell flows +- Dynamic upsell selection + +## Upsell Analytics + +Track upsell performance: + +```javascript +// Listen to upsell events +next.on('upsell:shown', (data) => { + // Track impression +}); + +next.on('upsell:accepted', (data) => { + // Track conversion +}); + +next.on('upsell:declined', (data) => { + // Track decline +}); +``` + +## Best Practices + +1. **Limit Steps**: Keep upsell flow to 2-3 steps max +2. **Fast Loading**: Optimize page load for upsells +3. **Mobile Friendly**: Ensure upsells work on mobile +4. **Clear Progress**: Show where user is in flow +5. **Easy Exit**: Always provide clear decline option + +## Advanced Configuration + +TODO: Add information about: +- Custom upsell logic +- Server-side upsell selection +- Personalized upsell offers +- Time-limited upsell windows \ No newline at end of file diff --git a/docs/campaign-cart/upsells/direct-upsells.md b/docs/campaign-cart/upsells/direct-upsells.md new file mode 100644 index 0000000..78d3187 --- /dev/null +++ b/docs/campaign-cart/upsells/direct-upsells.md @@ -0,0 +1,91 @@ +# Direct Upsells + +Simple yes/no decision for a single item. Package ID is on the container. + +## Basic Pattern + +```html + +
+

Add Extra Battery?

+

Extend your flight time with an extra battery pack

+ + +
+``` + +## With Product Display + +Show product details using display attributes: + +```html +
+ Extra Battery +

Extra Battery Pack

+

Price: $29.99

+

+ Save 50% +

+ + + +
+``` + +## Styling Examples + +### Card Style + +```html +
+
+

Special Offer!

+
+
+

Add protection for your purchase

+
+ Only $14.99 +
+
+
+ + +
+
+``` + +### Minimal Style + +```html +
+

Add expedited shipping for just $9.99?

+ + +
+``` + +## Best Practices + +1. **Clear Value Proposition**: Explain why they need the item +2. **Show Savings**: Highlight discounts or special pricing +3. **Easy to Decline**: Make "No" option clear and accessible +4. **Single Focus**: One product per direct upsell +5. **Urgency**: Mention if offer is time-limited + +## Action Attributes + +| Attribute | Description | +|-----------|-------------| +| `data-next-upsell-action="add"` | Accept upsell and add to order | +| `data-next-upsell-action="skip"` | Decline upsell and continue | + +## Container Attributes + +| Attribute | Description | +|-----------|-------------| +| `data-next-upsell="offer"` | Marks container as upsell offer | +| `data-next-package-id` | Package to add if accepted | \ No newline at end of file diff --git a/docs/campaign-cart/upsells/index.md b/docs/campaign-cart/upsells/index.md new file mode 100644 index 0000000..3b0565a --- /dev/null +++ b/docs/campaign-cart/upsells/index.md @@ -0,0 +1,28 @@ +--- +sidebar_label: Upsells +sidebar_position: 1 +--- + +# Upsells + +Maximize order value with post-purchase upsell flows. + +## Overview + +- [Upsells Overview](/docs/campaign-cart/upsells/overview) - Introduction to upsells + +## Upsell Types + +- [Direct Upsells](/docs/campaign-cart/upsells/direct-upsells) - Simple yes/no offers +- [Selection Upsells](/docs/campaign-cart/upsells/selection-upsells) - Multiple choice offers +- [Quantity Upsells](/docs/campaign-cart/upsells/quantity-upsells) - Quantity-based offers + +## Configuration + +- [Configuration](/docs/campaign-cart/upsells/configuration) - Advanced upsell settings + +## Related + +- [Examples - Upsell Flow](/docs/campaign-cart/examples/upsell-flow) - Complete upsell implementation +- [Cart System](/docs/campaign-cart/cart-system/overview) - Cart management basics + diff --git a/docs/campaign-cart/upsells/overview.md b/docs/campaign-cart/upsells/overview.md new file mode 100644 index 0000000..c662c07 --- /dev/null +++ b/docs/campaign-cart/upsells/overview.md @@ -0,0 +1,68 @@ +# Post Purchase Upsells Overview + +Maximize order value with post-purchase upsell flows. The SDK provides flexible patterns for presenting additional offers after checkout. + +## How Upsells Work + +1. Customer completes initial purchase +2. Redirect to upsell page(s) +3. Present relevant offers +4. Customer accepts or declines +5. Continue to next upsell or receipt + +## Configuration + +Set upsell URLs in meta tags: + +```html + + + +``` + +## Upsell Patterns + +### 1. Direct Upsell +Simple yes/no decision for a single item. + +### 2. Selection Upsell +Multiple options to choose from (cards or dropdown). + +### 3. Quantity Upsell +Let customers choose quantity before accepting. + +## Basic Example + +```html + +
+

Add Extra Battery Pack?

+

Never run out of power - 50% off!

+ + + +
+``` + +## Upsell Flow + +1. **Initial Order**: Customer completes checkout +2. **Upsell Page 1**: First offer presented +3. **Accept/Decline**: Customer makes choice +4. **Next Step**: Either next upsell or receipt page +5. **Order Update**: Accepted items added to order + +## Best Practices + +- Keep offers relevant to original purchase +- Highlight value and savings +- Make declining easy and clear +- Limit number of upsell steps +- Show order summary on upsell pages + +## Learn More + +- [Direct Upsells](direct-upsells.md) - Simple yes/no offers +- [Selection Upsells](selection-upsells.md) - Multiple choice offers +- [Quantity Upsells](quantity-upsells.md) - Quantity-based offers +- [Configuration](configuration.md) - Advanced upsell settings \ No newline at end of file diff --git a/docs/campaign-cart/upsells/quantity-upsells.md b/docs/campaign-cart/upsells/quantity-upsells.md new file mode 100644 index 0000000..6db0631 --- /dev/null +++ b/docs/campaign-cart/upsells/quantity-upsells.md @@ -0,0 +1,154 @@ +# Quantity Upsells + +Let customers choose quantity before accepting the upsell. + +## Quantity Controls Pattern + +Simple quantity selector for bulk purchases: + +```html + +
+

Add Extra Batteries?

+

Never run out of power during your flights

+ +
+ + 1 + +
+ +

Price: $29.99 each

+ + + +
+``` + +## Quantity Toggle Cards Pattern + +Click to select quantity - perfect for bundles: + +```html + +
+

Stock Up & Save!

+

Choose your battery bundle:

+ +
+
+

1 Pack

+

$29.99

+
+ +
+

2 Pack

+

$49.99

+ Save $10 +
+ +
+

4 Pack

+

$89.99

+ Best Value! +
+
+ + + +
+``` + +## Advanced Quantity Display + +Show dynamic pricing based on quantity: + +```html +
+

Replacement Filters

+ +
+ + + +
+ +
+

Unit Price: $9.99

+

Total: $1 × + 9.99 = + $9.99 +

+
+ + +
+``` + +## Quantity Attributes + +### Controls +- `data-next-upsell-quantity="increase"` - Increment button +- `data-next-upsell-quantity="decrease"` - Decrement button +- `data-next-upsell-quantity="display"` - Shows current quantity + +### Toggle Cards +- `data-next-upsell-quantity-toggle` - Set specific quantity on click +- Value is the quantity to set (e.g., "1", "2", "4") + +## Styling Quantity Cards + +```css +/* Style selected quantity card */ +.quantity-card[data-selected="true"] { + border: 2px solid #007bff; + background: #f0f8ff; +} + +/* Disable decrease at minimum */ +button[data-next-upsell-quantity="decrease"]:disabled { + opacity: 0.5; + cursor: not-allowed; +} +``` + +## Best Practices + +1. **Show Savings**: Highlight bulk discounts +2. **Display Total**: Show total price for quantity +3. **Preset Options**: Offer common quantities +4. **Visual Feedback**: Mark selected quantity +5. **Limits**: Set min/max quantity if needed + +## Common Patterns + +### Tiered Pricing +```html +
+ 1 for $10 +
+
+ 3 for $25 (Save $5) +
+
+ 5 for $40 (Save $10) +
+``` + +### Subscription Quantities +```html +
+ Monthly Supply - $29/mo +
+
+ 3 Month Supply - $79 (Save $8) +
+``` \ No newline at end of file diff --git a/docs/campaign-cart/upsells/selection-upsells.md b/docs/campaign-cart/upsells/selection-upsells.md new file mode 100644 index 0000000..ea1b152 --- /dev/null +++ b/docs/campaign-cart/upsells/selection-upsells.md @@ -0,0 +1,144 @@ +# Selection Upsells + +Multiple options to choose from. Uses selector ID instead of package ID. + +## Card Selection Pattern + +Let customers choose from multiple options: + +```html + +
+

Choose Your Protection Plan

+ +
+

Basic Protection

+

1 Year Coverage

+ $14.99 +
+ +
+

Premium Protection

+

2 Year Coverage + Accidental Damage

+ $24.99 +
+ +
+

Ultimate Protection

+

3 Year Coverage + Everything

+ $39.99 +
+ + + +
+``` + +## Dropdown Selection Pattern + +Compact selection using native select element: + +```html + +
+

Add Training Course?

+ + + + + +
+``` + +## Display Selection Info + +Show details about the selected option: + +```html +
+ + +
+

Selected: None

+

Price: $0

+

+ You save: $0 +

+
+ + +
+``` + +## Grid Layout Example + +```html +
+

Popular Accessories

+ +
+
+ Carrying Case +

Carrying Case

+ $19.99 +
+ +
+ Filter Set +

Filter Set

+ $39.99 +
+ +
+ Extra Props +

Extra Props

+ $24.99 +
+
+ + + +
+``` + +## Key Attributes + +### Container +- `data-next-upsell-selector` - Marks as selection upsell +- `data-next-selector-id` - Unique ID for this selector + +### Options +- `data-next-upsell-option` - Individual option card +- `data-next-package-id` - Package for this option +- `data-next-selected="true"` - Default selection + +### Dropdown +- `data-next-upsell-select` - Native select element +- Option `value` - Package ID for that option + +## CSS Classes + +Options automatically get classes: +- `.next-selected` - Currently selected option +- `.next-upsell-option` - All option cards + +## Best Practices + +1. **Highlight Best Value**: Mark recommended option +2. **Show Comparisons**: Display savings or features +3. **Default Selection**: Pre-select most popular option +4. **Clear Pricing**: Show price for each option +5. **Visual Hierarchy**: Make selection clear \ No newline at end of file diff --git a/docs/campaign-cart/utilities/analytics.md b/docs/campaign-cart/utilities/analytics.md new file mode 100644 index 0000000..b72fb44 --- /dev/null +++ b/docs/campaign-cart/utilities/analytics.md @@ -0,0 +1,319 @@ +# Analytics + +Track user interactions and conversions with the Next Commerce JS SDK event system. + +## Available Events + +The SDK emits various events you can listen to: + +```javascript +// SDK initialization +window.addEventListener('next:initialized', function() { + console.log('SDK is ready'); +}); + +// Cart events +next.on('cart:updated', (data) => { + console.log('Cart updated:', data); +}); + +next.on('cart:item-added', (data) => { + console.log('Item added to cart:', data); +}); + +next.on('cart:item-removed', (data) => { + console.log('Item removed from cart:', data); +}); + +// Selection events +next.on('selection:changed', (data) => { + console.log('Selection changed:', data); +}); + +// FOMO events +next.on('fomo:shown', (data) => { + console.log('FOMO notification shown:', data); +}); + +// Exit intent events +next.on('exit-intent:shown', (data) => { + console.log('Exit intent popup shown:', data); +}); + +next.on('exit-intent:clicked', (data) => { + console.log('Exit intent popup clicked:', data); +}); + +next.on('exit-intent:dismissed', (data) => { + console.log('Exit intent popup dismissed:', data); +}); +``` + +## Google Analytics 4 Integration + +```javascript +// Track cart events in GA4 +next.on('cart:item-added', (data) => { + if (typeof gtag !== 'undefined') { + gtag('event', 'add_to_cart', { + currency: data.currency || 'USD', + value: data.item.price, + items: [{ + item_id: data.item.packageId, + item_name: data.item.name, + price: data.item.price, + quantity: data.item.quantity + }] + }); + } +}); + +next.on('cart:item-removed', (data) => { + if (typeof gtag !== 'undefined') { + gtag('event', 'remove_from_cart', { + currency: data.currency || 'USD', + value: data.item.price, + items: [{ + item_id: data.item.packageId, + item_name: data.item.name, + price: data.item.price, + quantity: data.item.quantity + }] + }); + } +}); +``` + +## Facebook Pixel Integration + +```javascript +// Track events with Facebook Pixel +next.on('cart:item-added', (data) => { + if (typeof fbq !== 'undefined') { + fbq('track', 'AddToCart', { + content_ids: [data.item.packageId], + content_name: data.item.name, + content_type: 'product', + value: data.item.price, + currency: data.currency || 'USD' + }); + } +}); + +// Track selection changes +next.on('selection:changed', (data) => { + if (typeof fbq !== 'undefined') { + fbq('track', 'ViewContent', { + content_ids: [data.packageId], + content_name: data.name, + content_type: 'product', + value: data.price, + currency: 'USD' + }); + } +}); +``` + +## Custom Analytics Implementation + +```javascript +// Create custom analytics wrapper +const Analytics = { + track: function(event, properties) { + // Send to your analytics platform + console.log('Track:', event, properties); + + // Example: Send to custom endpoint + fetch('/api/analytics', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + event: event, + properties: properties, + timestamp: new Date().toISOString() + }) + }); + } +}; + +// Wire up SDK events to custom analytics +window.addEventListener('next:initialized', function() { + // Cart tracking + next.on('cart:updated', (data) => { + Analytics.track('Cart Updated', { + itemCount: data.itemCount, + total: data.total, + items: data.items + }); + }); + + // Conversion tracking + next.on('order:completed', (data) => { + Analytics.track('Purchase', { + orderId: data.orderId, + total: data.total, + items: data.items + }); + }); + + // Engagement tracking + next.on('fomo:clicked', (data) => { + Analytics.track('FOMO Clicked', { + product: data.product, + customer: data.customer + }); + }); +}); +``` + +## Enhanced Ecommerce Tracking + +```javascript +// Track product impressions +function trackProductImpressions() { + const products = document.querySelectorAll('[data-next-package-id]'); + const impressions = []; + + products.forEach((product, index) => { + const packageId = product.dataset.nextPackageId; + const name = product.querySelector('[data-next-display="package.name"]')?.textContent; + const price = product.querySelector('[data-next-display="package.price.raw"]')?.textContent; + + impressions.push({ + id: packageId, + name: name, + price: parseFloat(price) || 0, + position: index + 1 + }); + }); + + // Send to analytics + if (impressions.length > 0) { + Analytics.track('Product Impressions', { products: impressions }); + } +} + +// Track on page load +window.addEventListener('next:initialized', trackProductImpressions); +``` + +## Conversion Funnel Tracking + +```javascript +// Track each step of the funnel +const FunnelTracker = { + // Product view + trackProductView: function(packageId) { + Analytics.track('Product Viewed', { + packageId: packageId, + step: 1, + funnel: 'purchase' + }); + }, + + // Add to cart + trackAddToCart: function(data) { + Analytics.track('Added to Cart', { + packageId: data.packageId, + quantity: data.quantity, + price: data.price, + step: 2, + funnel: 'purchase' + }); + }, + + // Checkout started + trackCheckoutStarted: function() { + Analytics.track('Checkout Started', { + total: next.getCartData().total, + items: next.getCartData().items.length, + step: 3, + funnel: 'purchase' + }); + }, + + // Purchase complete + trackPurchase: function(orderData) { + Analytics.track('Purchase Completed', { + orderId: orderData.id, + total: orderData.total, + step: 4, + funnel: 'purchase' + }); + } +}; +``` + +## A/B Testing Events + +```javascript +// Track A/B test variations +const ABTest = { + // Track which variation user sees + trackVariation: function(testName, variation) { + Analytics.track('AB Test Viewed', { + testName: testName, + variation: variation + }); + }, + + // Track conversion for variation + trackConversion: function(testName, variation) { + Analytics.track('AB Test Converted', { + testName: testName, + variation: variation, + value: next.getCartData().total + }); + } +}; + +// Example: Test different button text +const buttonVariation = Math.random() > 0.5 ? 'A' : 'B'; +if (buttonVariation === 'A') { + document.querySelector('button').textContent = 'Add to Cart'; +} else { + document.querySelector('button').textContent = 'Buy Now'; +} +ABTest.trackVariation('button-text', buttonVariation); +``` + +## Best Practices + +1. **Track Key Events**: Focus on events that matter for your business +2. **Include Context**: Add relevant properties to events +3. **Avoid Over-Tracking**: Don't track every minor interaction +4. **Test Tracking**: Verify events fire correctly +5. **Privacy Compliance**: Respect user privacy preferences + +## Debugging Analytics + +```javascript +// Debug mode for analytics +const DEBUG_ANALYTICS = true; + +if (DEBUG_ANALYTICS) { + // Log all SDK events + const events = [ + 'cart:updated', 'cart:item-added', 'cart:item-removed', + 'selection:changed', 'fomo:shown', 'exit-intent:shown' + ]; + + events.forEach(event => { + next.on(event, (data) => { + console.group(`📊 Analytics Event: ${event}`); + console.log('Data:', data); + console.log('Timestamp:', new Date().toISOString()); + console.groupEnd(); + }); + }); +} +``` + +## Common Analytics Patterns + +TODO: Add information about: +- Revenue tracking +- User behavior analysis +- Cohort analysis +- Attribution tracking +- Multi-channel tracking \ No newline at end of file diff --git a/docs/campaign-cart/utilities/debugger.md b/docs/campaign-cart/utilities/debugger.md new file mode 100644 index 0000000..b186797 --- /dev/null +++ b/docs/campaign-cart/utilities/debugger.md @@ -0,0 +1,376 @@ +# Debug Mode + +The Next Commerce JS SDK includes a comprehensive debug overlay system that helps developers inspect state, monitor events, and troubleshoot issues during development. + +## Enabling Debug Mode + +Add the `?debugger=true` parameter to any page URL: + +``` +https://yoursite.com/checkout?debugger=true +``` + +This will automatically: +1. Load and display the debug overlay at the bottom of the page +2. Create the global `nextDebug` object for console access +3. Set the logger to DEBUG level for verbose logging + +**Note:** The `nextDebug` object is only available when debug mode is enabled via the URL parameter. If you try to access it without the parameter, you'll get a "nextDebug is not defined" error. + +## Debug Overlay Features + +The debug overlay provides multiple panels for inspecting different aspects of the SDK: + +### 1. Cart Panel +- View current cart state and items +- Inspect cart totals, discounts, and calculations +- Monitor cart updates in real-time +- Test cart operations directly + +### 2. Config Panel +- View current SDK configuration +- Inspect API keys and endpoints +- Check feature flags and settings +- Monitor configuration changes + +### 3. Campaign Panel +- View campaign data and settings +- Inspect product/package information +- Monitor campaign-specific state +- Test campaign operations + +### 4. Checkout Panel +- Monitor checkout form state +- Inspect validation errors +- View payment method selections +- Debug checkout flow issues + +### 5. Event Timeline Panel +- Real-time event monitoring +- Visual timeline of SDK events +- Event details and payloads +- Performance timing information + +### 6. Storage Panel +- Inspect localStorage data +- View sessionStorage contents +- Monitor storage changes +- Clear storage for testing + +## Using Debug Mode + +### Global Access + +When debug mode is active (via `?debugger=true`), a global `nextDebug` object is available in the console: + +```javascript +// Access debug overlay controls +nextDebug.overlay.show() +nextDebug.overlay.hide() +nextDebug.overlay.toggle() +nextDebug.overlay.isVisible() + +// Debug mode management +nextDebug.enableDebug() // Enables debug mode programmatically +nextDebug.disableDebug() // Disables debug mode and removes URL param +nextDebug.toggleDebug() // Toggles debug mode on/off +nextDebug.isDebugMode() // Returns true/false + +// Access to SDK stores +nextDebug.stores.cart // Cart store +nextDebug.stores.campaign // Campaign store +nextDebug.stores.config // Config store +nextDebug.stores.checkout // Checkout store +nextDebug.stores.order // Order store +nextDebug.stores.attribution // Attribution store + +// SDK instance +nextDebug.sdk // NextCommerce instance + +// Utility methods +nextDebug.reinitialize() // Reinitialize the SDK +nextDebug.getStats() // Get initialization statistics +``` + +### Keyboard Shortcuts + +When the debug overlay is visible: +- `ESC` - Toggle overlay visibility +- `Tab` - Switch between panels +- `↑/↓` - Navigate within panels + +### Panel-Specific Features + +#### Cart Panel Actions +- Add/remove test items +- Apply test discounts +- Clear cart +- Simulate cart errors + +#### Event Timeline +- Filter events by type +- Export event log +- Clear timeline +- Pause/resume monitoring + +#### Storage Panel +- Search storage keys +- Edit values directly +- Export/import storage +- Clear specific keys + +## Visual Debugging + +### X-Ray Mode +The debugger includes an X-Ray mode that highlights SDK-enhanced elements: +- Blue outline: Active SDK attributes +- Green outline: Conditional visibility elements +- Red outline: Error states +- Yellow outline: Loading states + +Toggle X-Ray mode from any panel's toolbar. + +### Performance Monitoring +- Track render times +- Monitor API call durations +- Identify performance bottlenecks +- View memory usage + +## Advanced Features + +### Cart Operations + +```javascript +// Add items to cart +nextDebug.addToCart(packageId, quantity) + +// Remove items from cart +nextDebug.removeFromCart(packageId) + +// Update quantity +nextDebug.updateQuantity(packageId, quantity) + +// Add test items (packages 2, 7, 9) +nextDebug.addTestItems() +``` + +### Campaign Inspection + +```javascript +// Reload campaign data +nextDebug.loadCampaign() + +// Clear campaign cache +nextDebug.clearCampaignCache() + +// Get cache information +nextDebug.getCacheInfo() + +// Inspect specific package details +nextDebug.inspectPackage(packageId) + +// Test shipping methods +nextDebug.testShippingMethod(methodId) +``` + +### Analytics Debugging + +```javascript +// Get analytics status +await nextDebug.analytics.getStatus() + +// View active providers +await nextDebug.analytics.getProviders() + +// Track custom event +await nextDebug.analytics.track('custom_event', { data: 'value' }) + +// Enable analytics debug mode +await nextDebug.analytics.setDebugMode(true) + +// Invalidate analytics context +await nextDebug.analytics.invalidateContext() +``` + +### Attribution Debugging + +```javascript +// Debug attribution data +nextDebug.attribution.debug() + +// Get attribution for API +nextDebug.attribution.get() + +// Set funnel name +nextDebug.attribution.setFunnel('FUNNEL_NAME') + +// Set Everflow click ID +nextDebug.attribution.setEvclid('click_id') + +// Get current funnel +nextDebug.attribution.getFunnel() + +// Clear persisted funnel +nextDebug.attribution.clearFunnel() +``` + +### Order Management + +```javascript +// View upsell journey +nextDebug.order.getJourney() + +// Check if order is expired +nextDebug.order.isExpired() + +// Clear order cache +nextDebug.order.clearCache() + +// Get order statistics +nextDebug.order.getStats() +``` + +### Accordion Controls + +```javascript +// Open accordion +nextDebug.accordion.open('accordion-id') + +// Close accordion +nextDebug.accordion.close('accordion-id') + +// Toggle accordion +nextDebug.accordion.toggle('accordion-id') +``` + +### Custom Event Logging + +```javascript +// Log custom events to the timeline +window.dispatchEvent(new CustomEvent('debug:log', { + detail: { + type: 'custom', + message: 'My debug message', + data: { /* any data */ } + } +})); + +// Update debug content +window.dispatchEvent(new CustomEvent('debug:update-content')); +``` + +### State Snapshots +- Save current state for later comparison +- Export state as JSON +- Import saved states for testing +- Compare state differences + +### Network Monitoring +- View API requests and responses +- Inspect request headers +- Monitor request timing +- Simulate network errors + +## Best Practices + +1. **Development Only** - Never enable debug mode in production +2. **Performance** - Debug mode adds overhead; disable when testing performance +3. **Privacy** - Debug overlay may display sensitive data +4. **Storage** - Clear debug storage regularly to avoid conflicts + +## Troubleshooting + +### Debug Overlay Not Appearing +1. Ensure `?debugger=true` is in the URL +2. Check browser console for errors +3. Verify SDK is properly loaded +4. Try hard refresh (Ctrl+F5) + +### Performance Issues +1. Disable event timeline if too many events +2. Clear storage panel data +3. Reduce update frequency in settings +4. Use panel-specific views instead of "All" + +### Console Errors + +#### nextDebug is undefined +This happens when debug mode is not enabled. Solutions: + +1. **Add the URL parameter:** + ``` + https://yoursite.com?debugger=true + ``` + +2. **Enable programmatically (if SDK is loaded):** + ```javascript + // First, check if the SDK is loaded + if (window.next) { + // Add debugger=true to URL and reload + const url = new URL(window.location.href); + url.searchParams.set('debugger', 'true'); + window.location.href = url.toString(); + } + ``` + +3. **For local development, use the debug config:** + ```javascript + // In your config.js + window.nextConfig = { + debug: true, + // other config... + }; + ``` + +#### Panel-specific errors +```javascript +// Refresh specific panel +nextDebug.overlay.updateContent() + +// Reinitialize debug overlay +nextDebug.reinitialize() +``` + +## Security Considerations + +- Debug mode exposes internal state and sensitive data +- Never use in production environments +- Be cautious when sharing debug screenshots +- Clear sensitive data from storage panel +- The debug overlay displays: + - API keys and configuration + - Cart and order details + - Customer information + - Analytics data + - Internal SDK state + +## Related URL Parameters + +For debugging purposes, you may also use: +- `?debugger=true` - Enable debug mode and overlay + +For functional URL parameters like `forcePackageId` and `ref_id`, see [URL Parameters](../getting-started/url-parameters.md). + +## Meta Tag Configuration + +You can also enable debug mode via meta tag: + +```html + +``` + +Or via JavaScript configuration: + +```javascript +window.nextConfig = { + debug: true +}; +``` + +## Integration with Dev Tools + +The debug overlay complements browser dev tools: +- Console logs include debug context +- Network tab shows SDK requests +- Elements panel highlights SDK attributes +- Performance profiler includes SDK marks \ No newline at end of file diff --git a/docs/campaign-cart/utilities/exit-intent.md b/docs/campaign-cart/utilities/exit-intent.md new file mode 100644 index 0000000..0237a58 --- /dev/null +++ b/docs/campaign-cart/utilities/exit-intent.md @@ -0,0 +1,473 @@ +# Exit Intent Popup + +Show popups when users try to leave the page. Supports both simple image popups and custom HTML templates. + +## Basic Usage (Image Popup) + +```javascript +// Wait for SDK to be fully initialized +window.addEventListener('next:initialized', function() { + console.log('SDK initialized, setting up exit intent...'); + + // Simple image popup setup + next.exitIntent({ + image: 'https://cdn.prod.website-files.com/68106277c04984fe676e423a/6823ba8d65474fce67152554_exit-popup1.webp', + action: async () => { + const result = await next.applyCoupon('SAVE10'); + if (result.success) { + alert('Coupon applied successfully: ' + result.message); + } else { + alert('Coupon failed: ' + result.message); + } + } + }); + + // Optional: Listen to events for analytics + next.on('exit-intent:shown', (data) => { + console.log('Exit intent popup shown:', data); + }); + + next.on('exit-intent:clicked', (data) => { + console.log('Exit intent popup clicked:', data); + }); + + next.on('exit-intent:dismissed', (data) => { + console.log('Exit intent popup dismissed:', data); + }); +}); +``` + +## Template-Based Usage (Custom HTML) + +```html + + + + +``` + +## Simple Examples + +### Just Show a Popup (No Action) + +```javascript +function justShowPopup() { + next.exitIntent({ + image: 'https://example.com/just-popup.webp' + }); +} +``` + +### Redirect Instead of Coupon + +```javascript +function redirectExample() { + next.exitIntent({ + image: 'https://example.com/special-offer.webp', + action: () => { + window.location.href = '/special-offer'; + } + }); +} +``` + +### Conditional Popup Based on Cart + +```javascript +function conditionalExample() { + const cartCount = next.getCartCount(); + + if (cartCount === 0) { + next.exitIntent({ + image: 'https://example.com/empty-cart.webp', + action: () => window.location.href = '/products' + }); + } else { + next.exitIntent({ + image: 'https://example.com/discount.webp', + action: () => next.applyCoupon('SAVE10') + }); + } +} +``` + +### Template with Multiple Offers + +```html + +``` + +## Configuration Options + +| Parameter | Description | Required | Default | +|-----------|-------------|----------|---------| +| `image` | URL of popup image | Yes (if no template) | - | +| `template` | Name of template (matches `data-template` attribute) | Yes (if no image) | - | +| `action` | Function to execute on click/custom action | No | - | +| `showCloseButton` | Show X button on modal | No | false | +| `overlayClosable` | Allow clicking overlay to close | No | true | +| `maxTriggers` | Maximum times to show per session | No | 1 | +| `disableOnMobile` | Disable on mobile devices | No | true | +| `mobileScrollTrigger` | Enable scroll trigger on mobile | No | false | +| `useSessionStorage` | Remember dismissal in session | No | true | +| `sessionStorageKey` | Custom storage key | No | 'exit-intent-dismissed' | + +## Events + +### exit-intent:shown +Fired when popup is displayed: +```javascript +next.on('exit-intent:shown', (data) => { + // data.imageUrl - The image URL shown (if using image) + // data.template - The template name (if using template) +}); +``` + +### exit-intent:clicked +Fired when popup is clicked (image mode only): +```javascript +next.on('exit-intent:clicked', (data) => { + // data.imageUrl - The image URL clicked +}); +``` + +### exit-intent:dismissed +Fired when popup is closed without action: +```javascript +next.on('exit-intent:dismissed', (data) => { + // data.imageUrl - The image URL (if using image) + // data.template - The template name (if using template) +}); +``` + +### exit-intent:closed +Fired when close button is clicked: +```javascript +next.on('exit-intent:closed', (data) => { + // data.template - The template name +}); +``` + +### exit-intent:action +Fired when template action buttons are clicked: +```javascript +next.on('exit-intent:action', (data) => { + // data.action - The action type ('close', 'apply-coupon', 'custom') + // data.couponCode - The coupon code (if action is 'apply-coupon') +}); +``` + +## Advanced Examples + +### Multiple Exit Intent Strategies + +```javascript +// Different popups for different pages +window.addEventListener('next:initialized', function() { + const pathname = window.location.pathname; + + if (pathname.includes('/product')) { + // Product page - offer discount + next.exitIntent({ + image: '/images/10-percent-off.jpg', + action: () => next.applyCoupon('SAVE10') + }); + } else if (pathname.includes('/cart')) { + // Cart page - free shipping offer with template + next.exitIntent({ + template: 'exit-intent-shipping', + showCloseButton: true + }); + } else { + // Other pages - newsletter signup + next.exitIntent({ + template: 'exit-intent-newsletter', + overlayClosable: true + }); + } +}); +``` + +### Cart Value Based Offers + +```javascript +function setupDynamicExitIntent() { + const cartData = next.getCartData(); + const cartTotal = cartData?.totals?.total?.value || 0; + + if (cartTotal === 0) { + // Empty cart - show bestsellers + next.exitIntent({ + image: '/images/bestsellers.jpg', + action: () => window.location.href = '/bestsellers' + }); + } else if (cartTotal < 50) { + // Small cart - offer percentage discount + next.exitIntent({ + template: 'exit-intent-discount-15' + }); + } else if (cartTotal < 100) { + // Medium cart - offer free shipping + next.exitIntent({ + template: 'exit-intent-free-shipping' + }); + } else { + // Large cart - offer free gift + next.exitIntent({ + image: '/images/free-gift-100.jpg', + action: () => { + next.addToCart({ packageId: 99, quantity: 1 }); // Free gift item + } + }); + } +} +``` + +### Time-Based Exit Intent + +```javascript +// Show exit intent only after user has been on page for 30 seconds +let exitIntentTimer; + +window.addEventListener('next:initialized', function() { + exitIntentTimer = setTimeout(() => { + next.exitIntent({ + image: '/images/dont-leave-yet.jpg', + action: () => next.applyCoupon('COMEBACK') + }); + }, 30000); // 30 seconds +}); + +// Clear timer if user completes action +function onUserAction() { + clearTimeout(exitIntentTimer); +} +``` + +## Template Action Attributes + +When using templates, you can add special attributes to buttons and links to trigger actions: + +### Available Actions + +| Attribute | Additional Attributes | Description | +|-----------|----------------------|-------------| +| `data-exit-intent-action="close"` | - | Closes the modal | +| `data-exit-intent-action="apply-coupon"` | `data-coupon-code="CODE"` | Applies coupon and closes modal | +| `data-exit-intent-action="custom"` | - | Triggers the custom action function from options | + +### ⚠️ Important: How Actions Work + +**For Templates:** +- The `action` function is **ONLY** triggered by elements with `data-exit-intent-action="custom"` +- Clicking regular content (divs, text, etc.) does NOT trigger any action +- You MUST add the attribute to buttons/links that should trigger the action + +**For Images:** +- Clicking anywhere on the image triggers the action function + +### Working Example: + +```html + + + +``` + +## Styling + +The exit intent popup can be styled with CSS: + +```css +/* Exit intent overlay */ +[data-exit-intent="overlay"] { + /* Overlay is styled inline but you can override */ + background: rgba(0, 0, 0, 0.8) !important; +} + +/* Exit intent popup container */ +[data-exit-intent="popup"] { + /* Popup container styles */ +} + +/* Template popup specific */ +.exit-intent-template-popup { + background: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); +} + +/* Close button */ +[data-exit-intent="close"] { + /* Close button styles */ +} + +/* Your custom template content */ +.exit-modal-content { + padding: 40px; + text-align: center; +} + +.exit-modal-content h2 { + margin-bottom: 20px; + font-size: 28px; +} + +.exit-modal-content button { + margin: 10px; + padding: 12px 30px; + font-size: 16px; + border-radius: 5px; +} +``` + +## Best Practices + +1. **Choose the Right Method**: + - Use **image popups** for simple, visual offers + - Use **templates** for interactive, complex modals + +2. **Mobile Optimization**: + - Default `disableOnMobile: true` for desktop-only + - Enable `mobileScrollTrigger` for mobile support + - Test template layouts on small screens + +3. **Session Management**: + - Use `maxTriggers: 1` to avoid annoying users + - Leverage `sessionStorage` to remember dismissals + - Reset with `next.exitIntent.reset()` if needed + +4. **Template Best Practices**: + - Keep templates hidden with `