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:
304
includes/Frontend/Assets.php
Normal file
304
includes/Frontend/Assets.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
namespace WooNooW\Frontend;
|
||||
|
||||
/**
|
||||
* Frontend Assets Manager
|
||||
* Handles loading of customer-spa assets
|
||||
*/
|
||||
class Assets {
|
||||
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
public static function init() {
|
||||
add_action('wp_enqueue_scripts', [self::class, 'enqueue_assets'], 20);
|
||||
add_action('wp_head', [self::class, 'add_inline_config'], 5);
|
||||
add_action('wp_enqueue_scripts', [self::class, 'dequeue_conflicting_scripts'], 100);
|
||||
add_filter('script_loader_tag', [self::class, 'add_module_type'], 10, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add type="module" to customer-spa scripts
|
||||
*/
|
||||
public static function add_module_type($tag, $handle, $src) {
|
||||
// Add type="module" to our Vite scripts
|
||||
if (strpos($handle, 'woonoow-customer') !== false) {
|
||||
$tag = str_replace('<script ', '<script type="module" ', $tag);
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue customer-spa assets
|
||||
*/
|
||||
public static function enqueue_assets() {
|
||||
// Only load on pages with WooNooW shortcodes or in full SPA mode
|
||||
if (!self::should_load_assets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if dev mode is enabled
|
||||
$is_dev = defined('WOONOOW_CUSTOMER_DEV') && WOONOOW_CUSTOMER_DEV;
|
||||
|
||||
if ($is_dev) {
|
||||
// Dev mode: Load from Vite dev server
|
||||
$dev_server = 'https://woonoow.local:5174';
|
||||
|
||||
// Vite client for HMR
|
||||
wp_enqueue_script(
|
||||
'woonoow-customer-vite',
|
||||
$dev_server . '/@vite/client',
|
||||
[],
|
||||
null,
|
||||
false // Load in header
|
||||
);
|
||||
|
||||
// Main entry point
|
||||
wp_enqueue_script(
|
||||
'woonoow-customer-spa',
|
||||
$dev_server . '/src/main.tsx',
|
||||
['woonoow-customer-vite'],
|
||||
null,
|
||||
false // Load in header
|
||||
);
|
||||
|
||||
error_log('WooNooW Customer: Loading from Vite dev server at ' . $dev_server);
|
||||
error_log('WooNooW Customer: Scripts enqueued - vite client and main.tsx');
|
||||
} else {
|
||||
// Production mode: Load from build
|
||||
$plugin_url = plugin_dir_url(dirname(dirname(__FILE__)));
|
||||
$dist_path = plugin_dir_path(dirname(dirname(__FILE__))) . 'customer-spa/dist/';
|
||||
|
||||
// Check if build exists
|
||||
if (!file_exists($dist_path)) {
|
||||
error_log('WooNooW: customer-spa build not found. Run: cd customer-spa && npm run build');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load manifest to get hashed filenames
|
||||
$manifest_file = $dist_path . 'manifest.json';
|
||||
if (file_exists($manifest_file)) {
|
||||
$manifest = json_decode(file_get_contents($manifest_file), true);
|
||||
|
||||
// Enqueue main JS
|
||||
if (isset($manifest['src/main.tsx'])) {
|
||||
$main_js = $manifest['src/main.tsx']['file'];
|
||||
wp_enqueue_script(
|
||||
'woonoow-customer-spa',
|
||||
$plugin_url . 'customer-spa/dist/' . $main_js,
|
||||
[],
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// Enqueue main CSS
|
||||
if (isset($manifest['src/main.tsx']['css'])) {
|
||||
foreach ($manifest['src/main.tsx']['css'] as $css_file) {
|
||||
wp_enqueue_style(
|
||||
'woonoow-customer-spa',
|
||||
$plugin_url . 'customer-spa/dist/' . $css_file,
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback for production build without manifest
|
||||
wp_enqueue_script(
|
||||
'woonoow-customer-spa',
|
||||
$plugin_url . 'customer-spa/dist/app.js',
|
||||
[],
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'woonoow-customer-spa',
|
||||
$plugin_url . 'customer-spa/dist/app.css',
|
||||
[],
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add inline config and scripts to page head
|
||||
*/
|
||||
public static function add_inline_config() {
|
||||
if (!self::should_load_assets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Customer SPA settings
|
||||
$spa_settings = get_option('woonoow_customer_spa_settings', []);
|
||||
$default_settings = [
|
||||
'mode' => 'disabled',
|
||||
'layout' => 'modern',
|
||||
'colors' => [
|
||||
'primary' => '#3B82F6',
|
||||
'secondary' => '#8B5CF6',
|
||||
'accent' => '#10B981',
|
||||
],
|
||||
'typography' => [
|
||||
'preset' => 'professional',
|
||||
],
|
||||
];
|
||||
$theme_settings = array_replace_recursive($default_settings, $spa_settings);
|
||||
|
||||
// Get WooCommerce currency settings
|
||||
$currency_settings = [
|
||||
'code' => get_woocommerce_currency(),
|
||||
'symbol' => get_woocommerce_currency_symbol(),
|
||||
'position' => get_option('woocommerce_currency_pos', 'left'),
|
||||
'thousandSeparator' => wc_get_price_thousand_separator(),
|
||||
'decimalSeparator' => wc_get_price_decimal_separator(),
|
||||
'decimals' => wc_get_price_decimals(),
|
||||
];
|
||||
|
||||
$config = [
|
||||
'apiUrl' => rest_url('woonoow/v1'),
|
||||
'nonce' => wp_create_nonce('wp_rest'),
|
||||
'siteUrl' => get_site_url(),
|
||||
'siteTitle' => get_bloginfo('name'),
|
||||
'user' => [
|
||||
'isLoggedIn' => is_user_logged_in(),
|
||||
'id' => get_current_user_id(),
|
||||
],
|
||||
'theme' => $theme_settings,
|
||||
'currency' => $currency_settings,
|
||||
];
|
||||
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
window.woonoowCustomer = <?php echo wp_json_encode($config); ?>;
|
||||
</script>
|
||||
<?php
|
||||
|
||||
// If dev mode, output scripts directly
|
||||
$is_dev = defined('WOONOOW_CUSTOMER_DEV') && WOONOOW_CUSTOMER_DEV;
|
||||
if ($is_dev) {
|
||||
$dev_server = 'https://woonoow.local:5174';
|
||||
?>
|
||||
<script type="module">
|
||||
import RefreshRuntime from '<?php echo $dev_server; ?>/@react-refresh'
|
||||
RefreshRuntime.injectIntoGlobalHook(window)
|
||||
window.$RefreshReg$ = () => {}
|
||||
window.$RefreshSig$ = () => (type) => type
|
||||
window.__vite_plugin_react_preamble_installed__ = true
|
||||
</script>
|
||||
<script type="module" crossorigin src="<?php echo $dev_server; ?>/@vite/client"></script>
|
||||
<script type="module" crossorigin src="<?php echo $dev_server; ?>/src/main.tsx"></script>
|
||||
<?php
|
||||
error_log('WooNooW Customer: Scripts output directly in head with React Refresh preamble');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should load customer-spa assets
|
||||
*/
|
||||
private static function should_load_assets() {
|
||||
global $post;
|
||||
|
||||
// Get Customer SPA settings
|
||||
$spa_settings = get_option('woonoow_customer_spa_settings', []);
|
||||
$mode = isset($spa_settings['mode']) ? $spa_settings['mode'] : 'disabled';
|
||||
|
||||
// If disabled, don't load
|
||||
if ($mode === 'disabled') {
|
||||
// Still check for shortcodes
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_shop')) {
|
||||
return true;
|
||||
}
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_cart')) {
|
||||
return true;
|
||||
}
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_checkout')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Full SPA mode - load on all WooCommerce pages
|
||||
if ($mode === 'full') {
|
||||
if (function_exists('is_shop') && is_shop()) {
|
||||
return true;
|
||||
}
|
||||
if (function_exists('is_product') && is_product()) {
|
||||
return true;
|
||||
}
|
||||
if (function_exists('is_cart') && is_cart()) {
|
||||
return true;
|
||||
}
|
||||
if (function_exists('is_checkout') && is_checkout()) {
|
||||
return true;
|
||||
}
|
||||
if (function_exists('is_account_page') && is_account_page()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checkout-Only mode - load only on specific pages
|
||||
if ($mode === 'checkout_only') {
|
||||
$checkout_pages = isset($spa_settings['checkoutPages']) ? $spa_settings['checkoutPages'] : [];
|
||||
|
||||
if (!empty($checkout_pages['checkout']) && function_exists('is_checkout') && is_checkout() && !is_order_received_page()) {
|
||||
return true;
|
||||
}
|
||||
if (!empty($checkout_pages['thankyou']) && function_exists('is_order_received_page') && is_order_received_page()) {
|
||||
return true;
|
||||
}
|
||||
if (!empty($checkout_pages['account']) && function_exists('is_account_page') && is_account_page()) {
|
||||
return true;
|
||||
}
|
||||
if (!empty($checkout_pages['cart']) && function_exists('is_cart') && is_cart()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if current page has WooNooW shortcodes
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_shop')) {
|
||||
return true;
|
||||
}
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_cart')) {
|
||||
return true;
|
||||
}
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_checkout')) {
|
||||
return true;
|
||||
}
|
||||
if ($post && has_shortcode($post->post_content, 'woonoow_account')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue conflicting scripts when SPA is active
|
||||
*/
|
||||
public static function dequeue_conflicting_scripts() {
|
||||
if (!self::should_load_assets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dequeue WooCommerce scripts that conflict with SPA
|
||||
wp_dequeue_script('wc-cart-fragments');
|
||||
wp_dequeue_script('woocommerce');
|
||||
wp_dequeue_script('wc-add-to-cart');
|
||||
wp_dequeue_script('wc-add-to-cart-variation');
|
||||
|
||||
// Dequeue WordPress block scripts that cause errors in SPA
|
||||
wp_dequeue_script('wp-block-library');
|
||||
wp_dequeue_script('wp-block-navigation');
|
||||
wp_dequeue_script('wp-interactivity');
|
||||
wp_dequeue_script('wp-interactivity-router');
|
||||
|
||||
// Keep only essential WooCommerce styles, dequeue others if needed
|
||||
// wp_dequeue_style('woocommerce-general');
|
||||
// wp_dequeue_style('woocommerce-layout');
|
||||
// wp_dequeue_style('woocommerce-smallscreen');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user