UNPKG

@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
# 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.