# Koia Eshop SDK

A configurable React context provider for e-commerce functionality with cart management, discount codes, order processing, and API client for product management.

## Features

- 🛒 **Cart Management**: Add, remove, and manage cart items
- 🏷️ **Discount Codes**: Configurable discount code system
- 💾 **Persistence**: Automatic localStorage persistence
- 🔧 **Configurable**: Customizable API endpoints and storage keys
- 📦 **TypeScript**: Full TypeScript support
- 🎯 **Framework Agnostic**: Works with any React setup
- 🌐 **API Client**: Built-in client for product management and API calls

## Installation

```bash
npm install @koia-ai/koia-eshop-sdk
```

or

```bash
yarn add @koia-ai/koia-eshop-sdk
```

## Quick Start

### React Context Provider (Cart Management)

```tsx
import { EshopProvider, useEshop } from '@koia-ai/koia-eshop-sdk';

// Configure your eshop
const config = {
  discountCodes: {
    'SAVE10': 10,
    'SAVE20': 20,
    'FREESHIP': 0 // Special code for free shipping
  },
  apiEndpoint: '/api/orders',
  storageKeys: {
    cart: 'my-cart',
    discountCode: 'my-discount',
    discountPercentage: 'my-discount-percentage'
  }
};

function App() {
  return (
    <EshopProvider config={config}>
      <YourApp />
    </EshopProvider>
  );
}

function ProductComponent() {
  const { addToCart } = useEshop();
  
  const handleAddToCart = () => {
    addToCart({
      id: 'product-1',
      name: 'Awesome Product',
      price: 29.99,
      quantity: 1
    });
  };

  return (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  );
}
```

### API Client (Product Management)

```tsx
import { useEshopClient } from '@koia-ai/koia-eshop-sdk';

function ProductList() {
  const eshop = useEshopClient(supabaseUrl, supabaseKey, baseUrl);
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(false);

  const fetchProducts = async (organizationId: string) => {
    setLoading(true);
    const { data, error } = await eshop.getProducts(organizationId);
    
    if (error) {
      console.error('Failed to fetch products:', error);
    } else {
      setProducts(data || []);
    }
    setLoading(false);
  };

  useEffect(() => {
    fetchProducts('your-organization-id');
  }, []);

  return (
    <div>
      {loading ? (
        <p>Loading products...</p>
      ) : (
        products.map(product => (
          <div key={product.id}>
            <h3>{product.name}</h3>
            <p>{product.description}</p>
            <p>${product.price}</p>
          </div>
        ))
      )}
    </div>
  );
}
```

## Configuration

### EshopConfig Interface

```typescript
interface EshopConfig {
  discountCodes?: Record<string, number>; // Discount code -> percentage
  apiEndpoint?: string; // API endpoint for order creation
  storageKeys?: {
    cart?: string; // localStorage key for cart
    discountCode?: string; // localStorage key for discount code
    discountPercentage?: string; // localStorage key for discount percentage
  };
}
```

### Default Configuration

```typescript
const defaultConfig: EshopConfig = {
  discountCodes: {},
  apiEndpoint: '/api/orders',
  storageKeys: {
    cart: 'cart',
    discountCode: 'discountCode',
    discountPercentage: 'discountPercentage'
  }
};
```

## API Reference

### EshopProvider

The main context provider component.

```tsx
<EshopProvider config={config}>
  {children}
</EshopProvider>
```

**Props:**
- `children`: React children components
- `config`: Optional configuration object

### useEshop Hook

Returns the eshop context with all available methods and state.

```tsx
const {
  cart,
  addToCart,
  removeFromCart,
  removeOneFromCart,
  clearCart,
  applyDiscountCode,
  removeDiscountCode,
  discountCode,
  discountPercentage,
  subtotal,
  total,
  shippingCost,
  setShippingCost,
  finalizeOrder
} = useEshop();
```

### EshopClient

Client class for making API calls with authentication.

```tsx
const eshop = useEshopClient(supabaseUrl, supabaseKey, baseUrl);

// Set authentication token
eshop.setAccessToken('your-access-token');

// Fetch products
const { data: products, error } = await eshop.getProducts(organizationId);

// Make custom API requests
const { data, error } = await eshop.makeRequest('/api/custom-endpoint');
```

### useEshopClient Hook

Hook to create and manage an EshopClient instance.

```tsx
const client = useEshopClient(supabaseUrl, supabaseKey, baseUrl);
```

## Cart Management

### Adding Items

```tsx
const { addToCart } = useEshop();

addToCart({
  id: 'product-1',
  name: 'Product Name',
  price: 29.99,
  quantity: 2
});
```

### Removing Items

```tsx
const { removeFromCart, removeOneFromCart } = useEshop();

// Remove all quantities of an item
removeFromCart('product-1');

// Remove one quantity of an item
removeOneFromCart('product-1');
```

### Clearing Cart

```tsx
const { clearCart } = useEshop();

clearCart(); // Clears cart and resets discount information
```

## Product Management

### Fetching Products

```tsx
const eshop = useEshopClient(supabaseUrl, supabaseKey, baseUrl);

const { data: products, error } = await eshop.getProducts(organizationId);

if (error) {
  console.error('Failed to fetch products:', error);
} else {
  console.log('Products:', products);
}
```

### Authentication

```tsx
const eshop = useEshopClient(supabaseUrl, supabaseKey, baseUrl);

// Set access token for authenticated requests
eshop.setAccessToken('your-access-token');

// Clear access token
eshop.clearAccessToken();
```

## Discount Codes

### Applying Discount Codes

```tsx
const { applyDiscountCode } = useEshop();

const handleApplyCode = (code: string) => {
  const isValid = applyDiscountCode(code);
  if (isValid) {
    console.log('Discount applied!');
  } else {
    console.log('Invalid discount code');
  }
};
```

### Removing Discount Codes

```tsx
const { removeDiscountCode } = useEshop();

removeDiscountCode(); // Removes current discount code
```

## Order Processing

### Finalizing Orders

```tsx
const { finalizeOrder } = useEshop();

const handleCheckout = async () => {
  try {
    const customerInfo = {
      name: 'John Doe',
      email: 'john@example.com',
      address: '123 Main St'
    };

    const result = await finalizeOrder(customerInfo);
    console.log('Order created:', result.order);
  } catch (error) {
    console.error('Order failed:', error);
  }
};
```

## State Values

### Cart Information

```tsx
const { cart, subtotal, total, shippingCost } = useEshop();

// cart: Array of CartItem objects
// subtotal: Sum of all items before discount and shipping
// total: Final total including discount and shipping
// shippingCost: Current shipping cost
```

### Discount Information

```tsx
const { discountCode, discountPercentage } = useEshop();

// discountCode: Current applied discount code (string | null)
// discountPercentage: Current discount percentage (number)
```

## Complete Example

```tsx
import React, { useState, useEffect } from 'react';
import { EshopProvider, useEshop, useEshopClient } from '@koia-ai/koia-eshop-sdk';

const config = {
  discountCodes: {
    'SAVE10': 10,
    'SAVE20': 20
  },
  apiEndpoint: '/api/orders'
};

function ProductList() {
  const eshop = useEshopClient(supabaseUrl, supabaseKey, baseUrl);
  const { addToCart } = useEshop();
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchProducts = async () => {
      const { data, error } = await eshop.getProducts('your-org-id');
      if (!error && data) {
        setProducts(data);
      }
    };
    fetchProducts();
  }, []);

  return (
    <div>
      {products.map(product => (
        <div key={product.id}>
          <h3>{product.name}</h3>
          <p>{product.description}</p>
          <p>${product.price}</p>
          <button onClick={() => addToCart({
            id: product.id,
            name: product.name,
            price: product.price,
            quantity: 1
          })}>
            Add to Cart
          </button>
        </div>
      ))}
    </div>
  );
}

function CartComponent() {
  const { 
    cart, 
    addToCart, 
    removeFromCart, 
    subtotal, 
    total,
    applyDiscountCode,
    discountCode 
  } = useEshop();
  
  const [discountInput, setDiscountInput] = useState('');

  const handleApplyDiscount = () => {
    const success = applyDiscountCode(discountInput);
    if (success) {
      setDiscountInput('');
    }
  };

  return (
    <div>
      <h2>Shopping Cart</h2>
      
      {cart.map(item => (
        <div key={item.id}>
          <span>{item.name}</span>
          <span>${item.price} x {item.quantity}</span>
          <button onClick={() => removeFromCart(item.id)}>Remove</button>
        </div>
      ))}
      
      <div>
        <p>Subtotal: ${subtotal}</p>
        <p>Total: ${total}</p>
        {discountCode && <p>Discount: {discountCode}</p>}
      </div>
      
      <div>
        <input
          value={discountInput}
          onChange={(e) => setDiscountInput(e.target.value)}
          placeholder="Enter discount code"
        />
        <button onClick={handleApplyDiscount}>Apply</button>
      </div>
    </div>
  );
}

function App() {
  return (
    <EshopProvider config={config}>
      <ProductList />
      <CartComponent />
    </EshopProvider>
  );
}
```

## Types

### CartItem

```typescript
interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}
```

### Product

```typescript
interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  organization_id: string;
  created_at: string;
  updated_at: string;
  organizations: {
    id: string;
    name: string;
    slug: string;
  };
}
```

### EshopContextType

```typescript
interface EshopContextType {
  cart: CartItem[];
  addToCart: (item: CartItem) => void;
  removeFromCart: (id: string) => void;
  removeOneFromCart: (id: string) => void;
  clearCart: () => void;
  applyDiscountCode: (code: string) => boolean;
  removeDiscountCode: () => void;
  discountCode: string | null;
  discountPercentage: number;
  subtotal: number;
  total: number;
  shippingCost: number;
  setShippingCost: (cost: number) => void;
  finalizeOrder: (customerInfo: any) => Promise<{ order: any }>;
}
```

### ApiResponse

```typescript
interface ApiResponse<T> {
  data?: T;
  error?: string;
}
```

## Error Handling

The `useEshop` hook will throw an error if used outside of an `EshopProvider`:

```tsx
try {
  const { cart } = useEshop();
} catch (error) {
  console.error('useEshop must be used within an EshopProvider');
}
```

API calls return consistent error format:

```tsx
const { data, error } = await eshop.getProducts(organizationId);

if (error) {
  console.error('API Error:', error);
} else {
  console.log('Products:', data);
}
```

## Browser Support

This package uses `localStorage` for persistence, which is supported in all modern browsers. For older browsers, consider using a polyfill.

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
