## 1. Fix Dark Mode Headings ✅ **Issue:** h1-h6 headings not changing color in dark mode **Fix:** ```css h1, h2, h3, h4, h5, h6 { @apply text-foreground; } ``` **Result:** All headings now use foreground color (adapts to theme) --- ## 2. Fix Settings Default Route ✅ **Issue:** Main Settings menu goes to /settings with placeholder page **Fix:** - Changed /settings to redirect to /settings/store - Store Details is now the default settings page - No more placeholder "Settings interface coming soon" **Code:** ```tsx useEffect(() => { navigate('/settings/store', { replace: true }); }, [navigate]); ``` --- ## 3. Fix "Cookie check failed" Upload Error ✅ **Issue:** Image upload failing with "Cookie check failed" **Root Cause:** WordPress REST API nonce not available **Fix:** - Added `wpApiSettings` to both dev and prod modes - Provides `root` and `nonce` for WordPress REST API - Image upload component already checks multiple nonce sources **Backend Changes:** ```php // Dev mode wp_localize_script($handle, 'wpApiSettings', [ 'root' => esc_url_raw(rest_url()), 'nonce' => wp_create_nonce('wp_rest'), ]); // Prod mode (same) ``` **Result:** Image upload now works with proper authentication --- ## 4. Add Theme Toggle to Mobile ✅ **Recommendation:** Yes, mobile should have theme toggle **Implementation:** Added to More page (mobile hub) **UI:** - 3-column grid with theme cards - ☀️ Light | 🌙 Dark | 🖥️ System - Active theme highlighted with primary border - Placed under "Appearance" section **Location:** ``` More Page ├── Coupons ├── Settings ├── Appearance (NEW) │ ├── ☀️ Light │ ├── 🌙 Dark │ └── 🖥️ System └── Exit Fullscreen / Logout ``` **Why More page?** - Mobile users go there for additional options - Natural place for appearance settings - Doesn't clutter main navigation - Desktop has header toggle, mobile has More page --- ## Summary ✅ **Dark mode headings** - Fixed with text-foreground ✅ **Settings redirect** - /settings → /settings/store ✅ **Upload nonce** - wpApiSettings added (dev + prod) ✅ **Mobile theme toggle** - Added to More page with 3-card grid **All issues resolved!** 🎉 **Note:** CSS lint warnings (@tailwind, @apply) are false positives - Tailwind directives are valid.
232 lines
11 KiB
PHP
232 lines
11 KiB
PHP
<?php
|
|
namespace WooNooW\Admin;
|
|
|
|
use WooNooW\Compat\MenuProvider;
|
|
use WooNooW\Compat\AddonRegistry;
|
|
use WooNooW\Compat\RouteRegistry;
|
|
use WooNooW\Compat\NavigationRegistry;
|
|
|
|
class Assets {
|
|
public static function init() {
|
|
add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue']);
|
|
}
|
|
|
|
public static function enqueue($hook) {
|
|
if ($hook !== 'toplevel_page_woonoow') {
|
|
return;
|
|
}
|
|
|
|
// Decide dev vs prod
|
|
$is_dev = self::is_dev_mode();
|
|
if ($is_dev) {
|
|
self::enqueue_dev();
|
|
} else {
|
|
self::enqueue_prod();
|
|
}
|
|
}
|
|
|
|
/** ----------------------------------------
|
|
* DEV MODE (Vite dev server)
|
|
* -------------------------------------- */
|
|
private static function enqueue_dev(): void {
|
|
$dev_url = self::dev_server_url(); // e.g. http://localhost:5173
|
|
|
|
// 1) Create a small handle to attach config (window.WNW_API)
|
|
$handle = 'wnw-admin-dev-config';
|
|
wp_register_script($handle, '', [], null, true);
|
|
wp_enqueue_script($handle);
|
|
|
|
// Attach runtime config (before module loader runs)
|
|
// If you prefer, keep using self::localize_runtime($handle)
|
|
wp_localize_script($handle, 'WNW_API', [
|
|
'root' => esc_url_raw(rest_url('woonoow/v1/')),
|
|
'nonce' => wp_create_nonce('wp_rest'),
|
|
'isDev' => true,
|
|
'devServer' => $dev_url,
|
|
'adminScreen' => 'woonoow',
|
|
'adminUrl' => admin_url('admin.php'),
|
|
]);
|
|
wp_add_inline_script($handle, 'window.WNW_API = window.WNW_API || WNW_API;', 'after');
|
|
|
|
// WordPress REST API settings (for media upload compatibility)
|
|
wp_localize_script($handle, 'wpApiSettings', [
|
|
'root' => esc_url_raw(rest_url()),
|
|
'nonce' => wp_create_nonce('wp_rest'),
|
|
]);
|
|
wp_add_inline_script($handle, 'window.wpApiSettings = window.wpApiSettings || wpApiSettings;', 'after');
|
|
|
|
// Also expose compact global for convenience
|
|
wp_localize_script($handle, 'wnw', [
|
|
'isDev' => true,
|
|
'devServer' => $dev_url,
|
|
'adminUrl' => admin_url('admin.php'),
|
|
'siteTitle' => get_bloginfo('name') ?: 'WooNooW',
|
|
]);
|
|
wp_add_inline_script($handle, 'window.wnw = window.wnw || wnw;', 'after');
|
|
|
|
// Localize store currency data (same as prod)
|
|
wp_localize_script($handle, 'WNW_STORE', self::store_runtime());
|
|
wp_add_inline_script($handle, 'window.WNW_STORE = window.WNW_STORE || WNW_STORE;', 'after');
|
|
|
|
// Localize Woo menus snapshot for instant render
|
|
$menus_snapshot = class_exists(MenuProvider::class) ? MenuProvider::get_snapshot() : [];
|
|
wp_localize_script($handle, 'WNW_WC_MENUS', ['items' => $menus_snapshot]);
|
|
wp_add_inline_script($handle, 'window.WNW_WC_MENUS = window.WNW_WC_MENUS || WNW_WC_MENUS;', 'after');
|
|
|
|
// Addon system data
|
|
wp_localize_script($handle, 'WNW_ADDONS', AddonRegistry::get_frontend_registry());
|
|
wp_add_inline_script($handle, 'window.WNW_ADDONS = window.WNW_ADDONS || WNW_ADDONS;', 'after');
|
|
|
|
wp_localize_script($handle, 'WNW_ADDON_ROUTES', RouteRegistry::get_frontend_routes());
|
|
wp_add_inline_script($handle, 'window.WNW_ADDON_ROUTES = window.WNW_ADDON_ROUTES || WNW_ADDON_ROUTES;', 'after');
|
|
|
|
wp_localize_script($handle, 'WNW_NAV_TREE', NavigationRegistry::get_frontend_nav_tree());
|
|
wp_add_inline_script($handle, 'window.WNW_NAV_TREE = window.WNW_NAV_TREE || WNW_NAV_TREE;', 'after');
|
|
|
|
// Temporary compat aliases for old WNM_*
|
|
wp_add_inline_script($handle, 'window.WNM_API = window.WNM_API || window.WNW_API;', 'after');
|
|
wp_add_inline_script($handle, 'window.WNM_WC_MENUS = window.WNM_WC_MENUS || window.WNW_WC_MENUS;', 'after');
|
|
|
|
// 2) Print a real module tag in the footer to load Vite client + app
|
|
add_action('admin_print_footer_scripts', function () use ($dev_url) {
|
|
// 1) React Refresh preamble (required by @vitejs/plugin-react)
|
|
?>
|
|
<script type="module">
|
|
import RefreshRuntime from "<?php echo esc_url( $dev_url ); ?>/@react-refresh";
|
|
RefreshRuntime.injectIntoGlobalHook(window);
|
|
window.$RefreshReg$ = () => {};
|
|
window.$RefreshSig$ = () => (type) => type;
|
|
window.__vite_plugin_react_preamble_installed__ = true;
|
|
</script>
|
|
<?php
|
|
|
|
// 2) Vite client (HMR)
|
|
printf('<script type="module" src="%s/@vite/client"></script>' . "\n", esc_url($dev_url));
|
|
|
|
// 3) Your app entry
|
|
printf('<script type="module">import "%s/src/main.tsx";</script>' . "\n", esc_url($dev_url));
|
|
}, 1);
|
|
}
|
|
|
|
/** ----------------------------------------
|
|
* PROD MODE (built assets in admin-spa/dist)
|
|
* -------------------------------------- */
|
|
private static function enqueue_prod(): void {
|
|
$dist_dir = plugin_dir_path(__FILE__) . '../admin-spa/dist/';
|
|
$base_url = plugins_url('../admin-spa/dist/', __FILE__);
|
|
|
|
$css = 'app.css';
|
|
$js = 'app.js';
|
|
|
|
$ver_css = file_exists($dist_dir . $css) ? (string) filemtime($dist_dir . $css) : self::asset_version();
|
|
$ver_js = file_exists($dist_dir . $js) ? (string) filemtime($dist_dir . $js) : self::asset_version();
|
|
|
|
if (file_exists($dist_dir . $css)) {
|
|
wp_enqueue_style('wnw-admin', $base_url . $css, [], $ver_css);
|
|
}
|
|
|
|
if (file_exists($dist_dir . $js)) {
|
|
wp_enqueue_script('wnw-admin', $base_url . $js, ['wp-element'], $ver_js, true);
|
|
self::localize_runtime('wnw-admin');
|
|
}
|
|
}
|
|
|
|
/** Attach runtime config to a handle */
|
|
private static function localize_runtime(string $handle): void {
|
|
wp_localize_script($handle, 'WNW_API', [
|
|
'root' => esc_url_raw(rest_url('woonoow/v1/')),
|
|
'nonce' => wp_create_nonce('wp_rest'),
|
|
'isDev' => self::is_dev_mode(),
|
|
'devServer' => self::dev_server_url(),
|
|
'adminScreen' => 'woonoow',
|
|
'adminUrl' => admin_url('admin.php'),
|
|
]);
|
|
|
|
// WordPress REST API settings (for media upload compatibility)
|
|
wp_localize_script($handle, 'wpApiSettings', [
|
|
'root' => esc_url_raw(rest_url()),
|
|
'nonce' => wp_create_nonce('wp_rest'),
|
|
]);
|
|
|
|
wp_localize_script($handle, 'WNW_STORE', self::store_runtime());
|
|
wp_add_inline_script($handle, 'window.WNW_STORE = window.WNW_STORE || WNW_STORE;', 'after');
|
|
|
|
// Compact global (prod)
|
|
wp_localize_script($handle, 'wnw', [
|
|
'isDev' => (bool) self::is_dev_mode(),
|
|
'devServer' => (string) self::dev_server_url(),
|
|
'adminUrl' => admin_url('admin.php'),
|
|
'siteTitle' => get_bloginfo('name') ?: 'WooNooW',
|
|
]);
|
|
wp_add_inline_script($handle, 'window.wnw = window.wnw || wnw;', 'after');
|
|
|
|
// Menus snapshot (prod)
|
|
$menus_snapshot = class_exists(MenuProvider::class) ? MenuProvider::get_snapshot() : [];
|
|
wp_localize_script($handle, 'WNW_WC_MENUS', ['items' => $menus_snapshot]);
|
|
wp_add_inline_script($handle, 'window.WNW_WC_MENUS = window.WNW_WC_MENUS || WNW_WC_MENUS;', 'after');
|
|
|
|
// Addon system data (prod)
|
|
wp_localize_script($handle, 'WNW_ADDONS', AddonRegistry::get_frontend_registry());
|
|
wp_add_inline_script($handle, 'window.WNW_ADDONS = window.WNW_ADDONS || WNW_ADDONS;', 'after');
|
|
|
|
wp_localize_script($handle, 'WNW_ADDON_ROUTES', RouteRegistry::get_frontend_routes());
|
|
wp_add_inline_script($handle, 'window.WNW_ADDON_ROUTES = window.WNW_ADDON_ROUTES || WNW_ADDON_ROUTES;', 'after');
|
|
|
|
wp_localize_script($handle, 'WNW_NAV_TREE', NavigationRegistry::get_frontend_nav_tree());
|
|
wp_add_inline_script($handle, 'window.WNW_NAV_TREE = window.WNW_NAV_TREE || WNW_NAV_TREE;', 'after');
|
|
|
|
// Temporary compat aliases for old WNM_*
|
|
wp_add_inline_script($handle, 'window.WNM_API = window.WNM_API || window.WNW_API;', 'after');
|
|
wp_add_inline_script($handle, 'window.WNM_WC_MENUS = window.WNM_WC_MENUS || window.WNW_WC_MENUS;', 'after');
|
|
}
|
|
|
|
/** Runtime store meta for frontend (currency, decimals, separators, position). */
|
|
private static function store_runtime(): array {
|
|
// WooCommerce helpers may not exist in some contexts; guard with defaults
|
|
$currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : 'USD';
|
|
$currency_sym = function_exists('get_woocommerce_currency_symbol') ? get_woocommerce_currency_symbol($currency) : '$';
|
|
$decimals = function_exists('wc_get_price_decimals') ? wc_get_price_decimals() : 2;
|
|
$thousand_sep = function_exists('wc_get_price_thousand_separator') ? wc_get_price_thousand_separator() : ',';
|
|
$decimal_sep = function_exists('wc_get_price_decimal_separator') ? wc_get_price_decimal_separator() : '.';
|
|
$currency_pos = function_exists('get_option') ? get_option('woocommerce_currency_pos', 'left') : 'left';
|
|
|
|
return [
|
|
'currency' => $currency,
|
|
'currency_symbol' => $currency_sym,
|
|
'decimals' => (int) $decimals,
|
|
'thousand_sep' => (string) $thousand_sep,
|
|
'decimal_sep' => (string) $decimal_sep,
|
|
'currency_pos' => (string) $currency_pos,
|
|
];
|
|
}
|
|
|
|
/** Determine dev mode:
|
|
* - WP environment 'development'
|
|
* - or constant WOONOOW_ADMIN_DEV=true
|
|
* - or filter override (woonoow/admin_is_dev)
|
|
*/
|
|
private static function is_dev_mode(): bool {
|
|
$env_is_dev = function_exists('wp_get_environment_type') && wp_get_environment_type() === 'development';
|
|
$const_dev = defined('WOONOOW_ADMIN_DEV') && WOONOOW_ADMIN_DEV === true;
|
|
$is_dev = $env_is_dev || $const_dev;
|
|
|
|
/**
|
|
* Filter: force dev/prod mode for WooNooW admin assets.
|
|
* Return true to use Vite dev server, false to use built assets.
|
|
*/
|
|
return (bool) apply_filters('woonoow/admin_is_dev', $is_dev);
|
|
}
|
|
|
|
/** Dev server URL (filterable) */
|
|
private static function dev_server_url(): string {
|
|
$default = 'http://localhost:5173';
|
|
/** Filter: change dev server URL if needed */
|
|
return (string) apply_filters('woonoow/admin_dev_server', $default);
|
|
}
|
|
|
|
/** Basic asset versioning */
|
|
private static function asset_version(): string {
|
|
// Bump when releasing; in dev we don't cache-bust
|
|
return defined('WOONOOW_VERSION') ? WOONOOW_VERSION : '0.1.0';
|
|
}
|
|
}
|