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
This commit is contained in:
Dwindi Ramadhana
2025-11-26 16:18:43 +07:00
parent 909bddb23d
commit f397ef850f
69 changed files with 12481 additions and 156 deletions

View File

@@ -0,0 +1,317 @@
<?php
namespace WooNooW\Api\Controllers;
use WP_REST_Controller;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
/**
* Settings Controller
* Handles Customer SPA settings via REST API
*/
class SettingsController extends WP_REST_Controller {
/**
* Namespace
*/
protected $namespace = 'woonoow/v1';
/**
* Rest base
*/
protected $rest_base = 'settings';
/**
* Register routes
*/
public function register_routes() {
// Get/Update Customer SPA settings
register_rest_route($this->namespace, '/' . $this->rest_base . '/customer-spa', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_customer_spa_settings'],
'permission_callback' => [$this, 'get_settings_permissions_check'],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'update_customer_spa_settings'],
'permission_callback' => [$this, 'update_settings_permissions_check'],
'args' => $this->get_customer_spa_schema(),
],
]);
}
/**
* Get Customer SPA settings
*/
public function get_customer_spa_settings(WP_REST_Request $request) {
$settings = $this->get_default_settings();
$saved_settings = get_option('woonoow_customer_spa_settings', []);
// Merge with saved settings
if (!empty($saved_settings)) {
$settings = array_replace_recursive($settings, $saved_settings);
}
// Add enabled flag
$settings['enabled'] = get_option('woonoow_customer_spa_enabled', false);
return new WP_REST_Response($settings, 200);
}
/**
* Update Customer SPA settings
*/
public function update_customer_spa_settings(WP_REST_Request $request) {
$params = $request->get_json_params();
// Extract enabled flag
if (isset($params['enabled'])) {
update_option('woonoow_customer_spa_enabled', (bool) $params['enabled']);
unset($params['enabled']);
}
// Get current settings
$current_settings = get_option('woonoow_customer_spa_settings', $this->get_default_settings());
// Merge with new settings
$new_settings = array_replace_recursive($current_settings, $params);
// Validate settings
$validated = $this->validate_settings($new_settings);
if (is_wp_error($validated)) {
return $validated;
}
// Save settings
update_option('woonoow_customer_spa_settings', $new_settings);
// Return updated settings
$new_settings['enabled'] = get_option('woonoow_customer_spa_enabled', false);
return new WP_REST_Response([
'success' => true,
'data' => $new_settings,
], 200);
}
/**
* Get default settings
*/
private function get_default_settings() {
return [
'mode' => 'disabled',
'checkoutPages' => [
'checkout' => true,
'thankyou' => true,
'account' => true,
'cart' => false,
],
'layout' => 'modern',
'branding' => [
'logo' => '',
'favicon' => '',
'siteName' => get_bloginfo('name'),
],
'colors' => [
'primary' => '#3B82F6',
'secondary' => '#8B5CF6',
'accent' => '#10B981',
'background' => '#FFFFFF',
'text' => '#1F2937',
],
'typography' => [
'preset' => 'professional',
'customFonts' => null,
],
'menus' => [
'primary' => 0,
'footer' => 0,
],
'homepage' => [
'sections' => [
[
'id' => 'hero-1',
'type' => 'hero',
'enabled' => true,
'order' => 0,
'config' => [],
],
[
'id' => 'featured-1',
'type' => 'featured',
'enabled' => true,
'order' => 1,
'config' => [],
],
[
'id' => 'categories-1',
'type' => 'categories',
'enabled' => true,
'order' => 2,
'config' => [],
],
],
],
'product' => [
'layout' => 'standard',
'showRelatedProducts' => true,
'showReviews' => true,
],
'checkout' => [
'style' => 'onepage',
'enableGuestCheckout' => true,
'showTrustBadges' => true,
'showOrderSummary' => 'sidebar',
],
];
}
/**
* Validate settings
*/
private function validate_settings($settings) {
// Validate mode
if (isset($settings['mode']) && !in_array($settings['mode'], ['disabled', 'full', 'checkout_only'])) {
return new WP_Error(
'invalid_mode',
__('Invalid mode. Must be disabled, full, or checkout_only.', 'woonoow'),
['status' => 400]
);
}
// Validate layout
if (isset($settings['layout']) && !in_array($settings['layout'], ['classic', 'modern', 'boutique', 'launch'])) {
return new WP_Error(
'invalid_layout',
__('Invalid layout. Must be classic, modern, boutique, or launch.', 'woonoow'),
['status' => 400]
);
}
// Validate colors (hex format)
if (isset($settings['colors'])) {
foreach ($settings['colors'] as $key => $color) {
if (!preg_match('/^#[a-fA-F0-9]{6}$/', $color)) {
return new WP_Error(
'invalid_color',
sprintf(__('Invalid color format for %s. Must be hex format (#RRGGBB).', 'woonoow'), $key),
['status' => 400]
);
}
}
}
// Validate typography preset
if (isset($settings['typography']['preset'])) {
$valid_presets = ['professional', 'modern', 'elegant', 'tech', 'custom'];
if (!in_array($settings['typography']['preset'], $valid_presets)) {
return new WP_Error(
'invalid_typography',
__('Invalid typography preset.', 'woonoow'),
['status' => 400]
);
}
}
return true;
}
/**
* Get Customer SPA settings schema
*/
private function get_customer_spa_schema() {
return [
'mode' => [
'type' => 'string',
'enum' => ['disabled', 'full', 'checkout_only'],
'description' => __('Customer SPA mode', 'woonoow'),
],
'checkoutPages' => [
'type' => 'object',
'properties' => [
'checkout' => ['type' => 'boolean'],
'thankyou' => ['type' => 'boolean'],
'account' => ['type' => 'boolean'],
'cart' => ['type' => 'boolean'],
],
],
'layout' => [
'type' => 'string',
'enum' => ['classic', 'modern', 'boutique', 'launch'],
'description' => __('Master layout', 'woonoow'),
],
'branding' => [
'type' => 'object',
'properties' => [
'logo' => ['type' => 'string'],
'favicon' => ['type' => 'string'],
'siteName' => ['type' => 'string'],
],
],
'colors' => [
'type' => 'object',
'properties' => [
'primary' => ['type' => 'string', 'pattern' => '^#[a-fA-F0-9]{6}$'],
'secondary' => ['type' => 'string', 'pattern' => '^#[a-fA-F0-9]{6}$'],
'accent' => ['type' => 'string', 'pattern' => '^#[a-fA-F0-9]{6}$'],
'background' => ['type' => 'string', 'pattern' => '^#[a-fA-F0-9]{6}$'],
'text' => ['type' => 'string', 'pattern' => '^#[a-fA-F0-9]{6}$'],
],
],
'typography' => [
'type' => 'object',
'properties' => [
'preset' => ['type' => 'string', 'enum' => ['professional', 'modern', 'elegant', 'tech', 'custom']],
'customFonts' => ['type' => ['object', 'null']],
],
],
'menus' => [
'type' => 'object',
'properties' => [
'primary' => ['type' => 'integer'],
'footer' => ['type' => 'integer'],
],
],
'homepage' => [
'type' => 'object',
'properties' => [
'sections' => ['type' => 'array'],
],
],
'product' => [
'type' => 'object',
'properties' => [
'layout' => ['type' => 'string'],
'showRelatedProducts' => ['type' => 'boolean'],
'showReviews' => ['type' => 'boolean'],
],
],
'checkout' => [
'type' => 'object',
'properties' => [
'style' => ['type' => 'string'],
'enableGuestCheckout' => ['type' => 'boolean'],
'showTrustBadges' => ['type' => 'boolean'],
'showOrderSummary' => ['type' => 'string'],
],
],
];
}
/**
* Check permissions for getting settings
*/
public function get_settings_permissions_check() {
return current_user_can('manage_woocommerce');
}
/**
* Check permissions for updating settings
*/
public function update_settings_permissions_check() {
return current_user_can('manage_woocommerce');
}
}