- Add WP Media Library integration for product and variation images - Support images array (URLs) conversion to attachment IDs - Add images array to API responses (Admin & Customer SPA) - Implement drag-and-drop sortable images in Admin product form - Add image gallery thumbnails in Customer SPA product page - Initialize WooCommerce session for guest cart operations - Fix product variations and attributes display in Customer SPA - Add variation image field in Admin SPA Changes: - includes/Api/ProductsController.php: Handle images array, add to responses - includes/Frontend/ShopController.php: Add images array for customer SPA - includes/Frontend/CartController.php: Initialize WC session for guests - admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function - admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images - admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field - customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
132 lines
3.3 KiB
TypeScript
132 lines
3.3 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' });
|
|
}
|
|
}
|
|
|
|
// API endpoints
|
|
const endpoints = {
|
|
shop: {
|
|
products: '/shop/products',
|
|
product: (id: number) => `/shop/products/${id}`,
|
|
categories: '/shop/categories',
|
|
search: '/shop/search',
|
|
},
|
|
cart: {
|
|
get: '/cart',
|
|
add: '/cart/add',
|
|
update: '/cart/update',
|
|
remove: '/cart/remove',
|
|
applyCoupon: '/cart/apply-coupon',
|
|
removeCoupon: '/cart/remove-coupon',
|
|
},
|
|
checkout: {
|
|
calculate: '/checkout/calculate',
|
|
create: '/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',
|
|
},
|
|
};
|
|
|
|
// Create singleton instance with endpoints
|
|
const client = new ApiClient();
|
|
|
|
// Export as apiClient with endpoints attached
|
|
export const apiClient = Object.assign(client, { endpoints });
|
|
|
|
// Also export individual pieces for convenience
|
|
export const api = client;
|
|
export { endpoints };
|