Files
WooNooW/customer-spa/src/lib/api/client.ts
Dwindi Ramadhana f397ef850f feat: Add product images support with WP Media Library integration
- 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
2025-11-26 16:18:43 +07:00

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 };