@koia-ai/koia-eshop-sdk
Version:
A configurable React context provider for e-commerce functionality with cart management, discount codes, order processing, and API client for product management
555 lines (436 loc) • 11.7 kB
Markdown
# 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.