Files
WooNooW/customer-spa/src/lib/api/client.ts
dwindown 909bddb23d feat: Create customer-spa core foundation (Sprint 1)
Sprint 1 - Foundation Complete! 

Created Core Files:
 src/main.tsx - Entry point
 src/App.tsx - Main app with routing
 src/index.css - Global styles (TailwindCSS)
 index.html - Development HTML

Pages Created (Placeholders):
 pages/Shop/index.tsx - Product listing
 pages/Product/index.tsx - Product detail
 pages/Cart/index.tsx - Shopping cart
 pages/Checkout/index.tsx - Checkout process
 pages/Account/index.tsx - My Account with sub-routes

Library Setup:
 lib/api/client.ts - API client with endpoints
 lib/cart/store.ts - Cart state management (Zustand)
 types/index.ts - TypeScript definitions

Configuration:
 .gitignore - Ignore node_modules, dist, logs
 README.md - Documentation

Features Implemented:

1. Routing (React Router v7)
   - /shop - Product listing
   - /shop/product/:id - Product detail
   - /shop/cart - Shopping cart
   - /shop/checkout - Checkout
   - /shop/account/* - My Account (dashboard, orders, profile)

2. API Client
   - Fetch wrapper with error handling
   - WordPress nonce authentication
   - Endpoints for shop, cart, checkout, account
   - TypeScript typed responses

3. Cart State (Zustand)
   - Add/update/remove items
   - Cart drawer (open/close)
   - LocalStorage persistence
   - Quantity management
   - Coupon support

4. Type Definitions
   - Product, Order, Customer types
   - Address, ShippingMethod, PaymentMethod
   - Cart, CartItem types
   - Window interface for WordPress globals

5. React Query Setup
   - QueryClient configured
   - 5-minute stale time
   - Retry on error
   - No refetch on window focus

6. Toast Notifications
   - Sonner toast library
   - Top-right position
   - Rich colors

Tech Stack:
- React 18 + TypeScript
- Vite (port 5174)
- React Router v7
- TanStack Query
- Zustand (state)
- TailwindCSS
- shadcn/ui
- React Hook Form + Zod

Dependencies Installed:
 437 packages installed
 All peer dependencies resolved
 Ready for development

Next Steps (Sprint 2):
- Implement Shop page with product grid
- Create ProductCard component
- Add filters and search
- Implement pagination
- Connect to WordPress API

Ready to run:
```bash
cd customer-spa
npm run dev
# Opens https://woonoow.local:5174
```
2025-11-21 13:53:38 +07:00

123 lines
3.0 KiB
TypeScript

/**
* API Client for WooNooW Customer SPA
* Handles all HTTP requests to WordPress REST API
*/
// Get API base URL from WordPress
const getApiBase = (): string => {
// @ts-ignore - WordPress global
return window.woonoowCustomer?.apiUrl || '/wp-json/woonoow/v1';
};
// Get nonce for authentication
const getNonce = (): string => {
// @ts-ignore - WordPress global
return window.woonoowCustomer?.nonce || '';
};
interface RequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
body?: any;
headers?: Record<string, string>;
}
class ApiClient {
private baseUrl: string;
constructor() {
this.baseUrl = getApiBase();
}
private async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
const { method = 'GET', body, headers = {} } = options;
const url = `${this.baseUrl}${endpoint}`;
const config: RequestInit = {
method,
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': getNonce(),
...headers,
},
credentials: 'same-origin',
};
if (body) {
config.body = JSON.stringify(body);
}
try {
const response = await fetch(url, config);
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }));
throw new Error(error.message || `HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('[API Error]', error);
throw error;
}
}
// GET request
async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
let url = endpoint;
if (params) {
const query = new URLSearchParams(params).toString();
url += `?${query}`;
}
return this.request<T>(url, { method: 'GET' });
}
// POST request
async post<T>(endpoint: string, body?: any): Promise<T> {
return this.request<T>(endpoint, { method: 'POST', body });
}
// PUT request
async put<T>(endpoint: string, body?: any): Promise<T> {
return this.request<T>(endpoint, { method: 'PUT', body });
}
// DELETE request
async delete<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint, { method: 'DELETE' });
}
}
// Export singleton instance
export const api = new ApiClient();
// Export API endpoints
export const endpoints = {
// Shop
products: '/shop/products',
product: (id: number) => `/shop/products/${id}`,
categories: '/shop/categories',
search: '/shop/search',
// Cart
cart: '/cart',
cartAdd: '/cart/add',
cartUpdate: '/cart/update',
cartRemove: '/cart/remove',
cartCoupon: '/cart/apply-coupon',
// Checkout
checkoutCalculate: '/checkout/calculate',
checkoutCreate: '/checkout/create-order',
paymentMethods: '/checkout/payment-methods',
shippingMethods: '/checkout/shipping-methods',
// Account
orders: '/account/orders',
order: (id: number) => `/account/orders/${id}`,
downloads: '/account/downloads',
profile: '/account/profile',
password: '/account/password',
addresses: '/account/addresses',
};