# Cart Component

The Cart component provides a slide-out drawer for managing shopping cart items, applying promo codes, and proceeding to checkout.

## Overview

The Cart component automatically:
- Displays cart items with images and details
- Groups items by retailer
- Shows real-time pricing and totals
- Supports quantity updates
- Handles promo code application
- Persists cart across sessions and tabs
- Provides checkout navigation

## Basic Usage

The cart is automatically available when using the SDK - no explicit injection needed for the drawer. However, you need a way for users to open it.

### Cart Buttons

#### Declarative Cart Button

Add a cart button using data attributes on the SDK script:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-cart-button="header-cart"
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>

<div id="header-cart"></div>
```

#### Cart Button with Item Count Badge

Show the number of items in the cart:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-cart-badge-button="header-cart"
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>

<div id="header-cart"></div>
```

#### Floating Cart Button

Create a floating button (bottom-right corner):

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-cart-button
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>
```

Leaving `data-cart-button` empty creates a floating button automatically.

#### Positional Cart Button

Control where the cart button appears:

```html
<!-- Inside an element -->
<script data-cart-button="inside:#header"></script>

<!-- Above an element -->
<script data-cart-button="above:.navigation"></script>

<!-- Below an element -->
<script data-cart-button="below:#logo"></script>

<!-- Replace an element -->
<script data-cart-button="replace:#cart-placeholder"></script>
```

#### Hide Cart Button

If you want to control cart opening manually:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-cart-button-hidden
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>
```

#### Programmatic Cart Button

Create cart buttons using JavaScript:

```javascript
// Button in a container
window.LiquidCommerce.elements.ui.cartButton('my-container', false);  // No badge

// Button with item count
window.LiquidCommerce.elements.ui.cartButton('my-container', true);  // With badge

// Floating button
window.LiquidCommerce.elements.ui.floatingCartButton(true);  // With badge
```

### Custom Toggle Buttons

Use your own button to toggle the cart:

```html
<button data-lce-cart-toggle-button>
  View Cart
</button>
```

The SDK automatically adds click handlers to elements with `data-lce-cart-toggle-button`.

## Cart Actions

### Open Cart

```javascript
window.LiquidCommerce.elements.actions.cart.openCart();
```

### Close Cart

```javascript
window.LiquidCommerce.elements.actions.cart.closeCart();
```

### Toggle Cart

```javascript
window.LiquidCommerce.elements.actions.cart.toggleCart();
```

### Add Product to Cart

Add products programmatically:

```javascript
await window.LiquidCommerce.elements.actions.cart.addProduct([
  {
    identifier: '00619947000020',
    fulfillmentType: 'shipping',  // or 'onDemand'
    quantity: 2
  },
  {
    identifier: '08504405135',
    fulfillmentType: 'onDemand',
    quantity: 1
  }
], true);  // true = open cart after adding
```

**Parameters:**
- `identifier` (string): Product UPC, SKU, or ID
- `fulfillmentType` (string): `'shipping'` or `'onDemand'`
- `quantity` (number): Number of items (default: 1)
- Second parameter (boolean): Open cart after adding (default: false)

### Apply Promo Code

```javascript
await window.LiquidCommerce.elements.actions.cart.applyPromoCode('SUMMER20');
```

The SDK automatically:
- Validates the promo code
- Calculates discount
- Updates cart totals
- Shows success/error message

### Remove Promo Code

```javascript
await window.LiquidCommerce.elements.actions.cart.removePromoCode();
```

### Reset Cart

Clear all items from the cart:

```javascript
await window.LiquidCommerce.elements.actions.cart.resetCart();
```

### Get Cart Details

Retrieve current cart information:

```javascript
const cartData = window.LiquidCommerce.elements.actions.cart.getDetails();

console.log(cartData);
// {
//   cartId: 'cart_abc123',
//   items: [...],
//   subtotal: 9998,  // in cents
//   total: 9998,
//   itemsCount: 3,
//   itemsQuantity: 5,
//   promoCode: { code: 'SUMMER20', discount: 1000 },
//   retailers: [...],
//   ...
// }
```

## Cart UI Helpers

### Display Cart Subtotal

Show cart subtotal anywhere on your page:

```html
<div>
  Cart Total: <span id="cart-total"></span>
</div>

<script>
window.addEventListener('lce:actions.client_ready', () => {
  window.LiquidCommerce.elements.ui.cartSubtotal('cart-total');
});
</script>
```

The element automatically updates when the cart changes.

### Display Items Count

Show the number of items in the cart:

```html
<div>
  Items: <span id="items-count"></span>
</div>

<script>
window.addEventListener('lce:actions.client_ready', () => {
  window.LiquidCommerce.elements.ui.cartItemsCount('items-count', {
    hideZero: true  // Hide when cart is empty
  });
});
</script>
```

Or using data attributes:

```html
<!-- Always show count, even when 0 -->
<span data-lce-cart-items-count="keep-zero"></span>

<!-- Hide when 0 (default behavior) -->
<span data-lce-cart-items-count></span>
```

## Events

### Cart Loaded

Fired when cart data is loaded:

```javascript
window.addEventListener('lce:actions.cart_loaded', (event) => {
  const { cartId, itemsCount, subtotal } = event.detail.data;
  console.log(`Cart loaded: ${itemsCount} items, total: $${subtotal / 100}`);
});
```

### Cart Opened/Closed

```javascript
window.addEventListener('lce:actions.cart_opened', (event) => {
  console.log('Cart drawer opened');
});

window.addEventListener('lce:actions.cart_closed', (event) => {
  console.log('Cart drawer closed');
});
```

### Cart Updated

Fired whenever cart contents change:

```javascript
window.addEventListener('lce:actions.cart_updated', (event) => {
  const { cartId, itemsCount, subtotal } = event.detail.data;
  console.log('Cart updated:', itemsCount, 'items');
});
```

### Item Added

```javascript
window.addEventListener('lce:actions.cart_item_added', (event) => {
  const { cartId, itemId, quantity } = event.detail.data;
  console.log(`Item ${itemId} added (quantity: ${quantity})`);
});
```

### Item Removed

```javascript
window.addEventListener('lce:actions.cart_item_removed', (event) => {
  const { cartId, itemId } = event.detail.data;
  console.log(`Item ${itemId} removed`);
});
```

### Quantity Changed

```javascript
window.addEventListener('lce:actions.cart_item_quantity_increase', (event) => {
  const { cartId, itemId, newQuantity } = event.detail.data;
  console.log(`Item ${itemId} quantity increased to ${newQuantity}`);
});

window.addEventListener('lce:actions.cart_item_quantity_decrease', (event) => {
  const { cartId, itemId, newQuantity } = event.detail.data;
  console.log(`Item ${itemId} quantity decreased to ${newQuantity}`);
});
```

### Promo Code Events

```javascript
window.addEventListener('lce:actions.cart_promo_code_applied', (event) => {
  const { cartId, discount, newSubtotal } = event.detail.data;
  console.log(`Promo code applied: $${discount / 100} off`);
});

window.addEventListener('lce:actions.cart_promo_code_removed', (event) => {
  const { cartId, newSubtotal } = event.detail.data;
  console.log('Promo code removed');
});

window.addEventListener('lce:actions.cart_promo_code_failed', (event) => {
  const { cartId, error } = event.detail.data;
  console.error('Promo code failed:', error);
});
```

### Product Add Success/Failure

```javascript
window.addEventListener('lce:actions.cart_product_add_success', (event) => {
  const { cartId, itemsAdded, identifiers } = event.detail.data;
  console.log(`Successfully added ${itemsAdded} products`);
});

window.addEventListener('lce:actions.cart_product_add_failed', (event) => {
  const { cartId, identifiers, error } = event.detail.data;
  console.error('Failed to add products:', error);
});
```

### Cart Reset

```javascript
window.addEventListener('lce:actions.cart_reset', (event) => {
  console.log('Cart has been reset');
});
```

## Customization

### Theme Configuration

Customize cart appearance:

```javascript
const client = await Elements('YOUR_API_KEY', {
  env: 'production',
  customTheme: {
    cart: {
      theme: {
        backgroundColor: '#ffffff'
      },
      layout: {
        showQuantityCounter: true,
        quantityCounterStyle: 'outlined',  // or 'input'
        drawerHeaderText: 'My Bag',
        goToCheckoutButtonText: 'Checkout'
      }
    }
  }
});
```

## State Persistence

### Local Storage

The cart automatically persists to `localStorage`

This allows the cart to survive:
- Page refreshes
- Browser restarts
- Navigation between pages

### Cross-Tab Synchronization

When you update the cart in one tab, all other open tabs automatically sync:

```javascript
// Tab 1: Add item
await window.LiquidCommerce.elements.actions.cart.addProduct([...]);

// Tab 2: Cart automatically updates
// No additional code needed
```

The SDK uses the following mechanisms:
- localStorage events
- Periodic polling
- API-based fallback

### Session Duration

Carts persist for 45 days by default. After 45 days of inactivity, the cart is cleared.

## Address Requirement

The cart requires a delivery address for:
- Accurate availability
- Correct pricing
- Shipping calculations

### Automatic Address Prompt

If no address is set when adding to cart, the SDK:
1. Opens address input drawer
2. Waits for user to set address
3. Retries the add-to-cart operation

### Pre-set Address

Set address before adding to cart:

```javascript
await window.LiquidCommerce.elements.actions.address.setAddressByPlacesId('ChIJ...');

// Now add to cart
await window.LiquidCommerce.elements.actions.cart.addProduct([...]);
```

## Promo Codes

### Configuration

Enable/disable promo codes globally:

```javascript
customTheme: {
  global: {
    layout: {
      allowPromoCodes: true  // or false to disable
    }
  }
}
```

### Promo Ticker

Show promotional codes in a scrolling ticker:

```javascript
const client = await Elements('YOUR_API_KEY', {
  env: 'production',
  promoTicker: [
    {
      promoCode: 'SUMMER20',
      text: ['20% Off Summer Sale', 'Free Shipping on Orders Over $50'],
      separator: '•',
      activeFrom: '2024-06-01T00:00:00Z',
      activeUntil: '2024-08-31T23:59:59Z'
    }
  ]
});
```

Or with data attributes:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-promo-code="SUMMER20"
  data-promo-text="20% Off Summer Sale | Free Shipping on Orders Over $50"
  data-promo-separator="•"
  data-promo-active-from="2024-06-01T00:00:00Z"
  data-promo-active-until="2024-08-31T23:59:59Z"
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>
```

### Auto-Apply from URL

Apply promo codes automatically via URL parameters:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-promo-code-param="lce_promo"
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>
```

Now visiting `https://yoursite.com?lce_promo=SUMMER20` automatically applies the code.

**Security Note:** Query parameters must start with `lce_` prefix to prevent conflicts with your application.

## Retailer Grouping

Items are automatically grouped by retailer in the cart:

```
┌─────────────────────────────────────┐
│ Cart (3 items)                      │
├─────────────────────────────────────┤
│ Spirits Shop                        │
│   • Premium Whiskey (750ml)    $49  │
│   • Bourbon Bottle (1L)        $65  │
│                                      │
│ Wine & More                         │
│   • Red Wine (750ml)           $25  │
├─────────────────────────────────────┤
│ Subtotal:                     $139  │
│ Promo Code (SUMMER20):        -$28  │
│ Total:                        $111  │
└─────────────────────────────────────┘
```

## Checkout Navigation

### Checkout Drawer (Default)

By default, clicking "Checkout" opens the checkout drawer:

```javascript
// User clicks checkout
// Checkout drawer slides in
```

### Checkout Page (Custom URL)

Redirect to a custom checkout page:

```javascript
const client = await Elements('YOUR_API_KEY', {
  env: 'production',
  checkout: {
    pageUrl: 'https://yoursite.com/checkout/{token}'
  }
});
```

Or with data attributes:

```html
<script
  defer
  data-liquid-commerce-elements
  data-token="YOUR_API_KEY"
  data-env="production"
  data-checkout-url="https://yoursite.com/checkout/{token}"
  type="text/javascript"
  src="https://elements.reservebar-worker.workers.dev/all/elements.js"
></script>
```

The `{token}` placeholder is replaced with the session checkout token.

## Use Cases

### E-commerce Site

```javascript
// Track cart events for analytics
window.addEventListener('lce:actions.cart_item_added', (event) => {
  gtag('event', 'add_to_cart', {
    value: event.detail.data.price / 100,
    currency: 'USD'
  });
});

window.addEventListener('lce:actions.cart_updated', (event) => {
  // Update header cart count
  document.getElementById('cart-badge').textContent = 
    event.detail.data.itemsCount;
});
```

### Marketing Campaign

```javascript
// Check URL for campaign parameter
const params = new URLSearchParams(window.location.search);
const campaign = params.get('campaign');

if (campaign === 'summer-sale') {
  await window.LiquidCommerce.elements.actions.cart.applyPromoCode('SUMMER20');
  window.LiquidCommerce.elements.actions.cart.openCart();
}
```

### Abandoned Cart Recovery

```javascript
// Store cart ID in your system
window.addEventListener('lce:actions.cart_updated', (event) => {
  const { cartId, itemsCount } = event.detail.data;
  
  if (itemsCount > 0) {
    // Send cart ID to your backend for abandoned cart emails
    fetch('/api/track-cart', {
      method: 'POST',
      body: JSON.stringify({ cartId, userId: currentUserId })
    });
  }
});
```

## Best Practices

### Initialize Cart Early

Pre-load cart on page load for faster access:

```javascript
window.addEventListener('lce:actions.client_ready', () => {
  // Cart loads automatically, but you can pre-fetch if needed
  window.LiquidCommerce.elements.actions.cart.getDetails();
});
```

### Provide Visual Feedback

Show loading states when adding to cart:

```javascript
async function addToCart(productId) {
  const button = document.getElementById('add-to-cart-btn');
  button.disabled = true;
  button.textContent = 'Adding...';
  
  try {
    await window.LiquidCommerce.elements.actions.cart.addProduct([
      { identifier: productId, fulfillmentType: 'shipping', quantity: 1 }
    ], true);
    
    button.textContent = 'Added!';
    setTimeout(() => {
      button.disabled = false;
      button.textContent = 'Add to Cart';
    }, 2000);
  } catch (error) {
    button.textContent = 'Failed';
    button.disabled = false;
  }
}
```

### Handle Empty Cart

Show appropriate messaging for empty carts:

```javascript
window.addEventListener('lce:actions.cart_updated', (event) => {
  const { itemsCount } = event.detail.data;
  
  if (itemsCount === 0) {
    // Show empty state in your UI
    document.getElementById('cart-status').textContent = 
      'Your cart is empty';
  }
});
```

## Troubleshooting

### Cart Not Opening

1. Check that SDK is initialized: `console.log(window.LiquidCommerce.elements)`
2. Verify no JavaScript errors in console
3. Ensure cart button is properly configured
4. Try manually: `window.LiquidCommerce.elements.actions.cart.openCart()`

### Items Not Adding

1. Check that address is set: `window.LiquidCommerce.elements.actions.address.getDetails()`
2. Verify product identifier is valid
3. Check fulfillment type is available for the product
4. Look for errors in browser console

### Promo Code Not Working

1. Verify promo codes are enabled in configuration
2. Check code is valid and active
3. Ensure code hasn't already been applied
4. Check console for error messages

## See Also

- [Product Component](./product-component.md) - Add products to cart
- [Checkout Component](./checkout-component.md) - Complete purchase
- [Actions API](../api/actions/cart-actions.md) - Cart actions reference
- [Events](./events.md) - All available events
- [Theming](./theming.md) - Customize appearance
