Files
WooNooW/includes/Admin/StandaloneAdmin.php
dwindown 9c5bdebf6f fix: Complete UI/UX polish - all 7 issues resolved
##  Issue 1: Customers Submenu Missing in WP-Admin
**Problem:** Tax and Customer submenus only visible in standalone mode
**Root Cause:** PHP navigation registry did not include Customers
**Fixed:** Added Customers to NavigationRegistry.php settings children
**Result:** Customers submenu now shows in all modes

##  Issue 2: App Logo/Title in Topbar
**Problem:** Should show logo → store name → "WooNooW" fallback
**Fixed:** Header component now:
- Fetches branding from /store/branding endpoint
- Shows logo image if available
- Falls back to store name text
- Updates on store settings change event
**Result:** Proper branding hierarchy in app header

##  Issue 3: Zone Card Header Density on Mobile
**Problem:** "Indonesia Addons" row with 3 icons too cramped on mobile
**Fixed:** Shipping.tsx zone card header:
- Reduced gap from gap-3 to gap-2/gap-1 on mobile
- Smaller font size on mobile (text-sm md:text-lg)
- Added min-w-0 for proper text truncation
- flex-shrink-0 on icon buttons
**Result:** Better mobile spacing and readability

##  Issue 4: Go to WP Admin Button
**Problem:** Should show in standalone mode, not wp-admin
**Fixed:** More page now shows "Go to WP Admin" button:
- Only in standalone mode
- Before Logout button
- Links to /wp-admin
**Result:** Easy access to WP Admin from standalone mode

##  Issue 5: Customer Settings 403 Error
**Problem:** Permission check failing for customer-settings endpoint
**Fixed:** StoreController.php check_permission():
- Added fallback: manage_woocommerce OR manage_options
- Ensures administrators always have access
**Result:** Customer Settings page loads successfully

##  Issue 6: Dark Mode Logo Upload Field
**Problem:** No UI to upload dark mode logo
**Fixed:** Store settings page now has:
- "Store logo (Light mode)" field
- "Store logo (Dark mode)" field (optional)
- Backend support in StoreSettingsProvider
- Full save/load functionality
**Result:** Users can upload separate logos for light/dark modes

##  Issue 7: Login Card Background Too Dark
**Problem:** Login card same color as background in dark mode
**Fixed:** Login.tsx card styling:
- Changed from dark:bg-gray-800 (solid)
- To dark:bg-gray-900/50 (semi-transparent)
- Added backdrop-blur-xl for glass effect
- Added border for definition
**Result:** Login card visually distinct with modern glass effect

---

## Summary

**All 7 Issues Resolved:**
1.  Customers submenu in all modes
2.  Logo/title hierarchy in topbar
3.  Mobile zone card spacing
4.  Go to WP Admin in standalone
5.  Customer Settings permission fix
6.  Dark mode logo upload field
7.  Lighter login card background

**Files Modified:**
- NavigationRegistry.php - Added Customers to nav
- App.tsx - Logo/branding in header
- Shipping.tsx - Mobile spacing
- More/index.tsx - WP Admin button
- StoreController.php - Permission fallback
- Store.tsx - Dark logo field
- StoreSettingsProvider.php - Dark logo backend
- Login.tsx - Card background

**Ready for production!** 🎉
2025-11-11 09:49:31 +07:00

173 lines
5.5 KiB
PHP

<?php
namespace WooNooW\Admin;
/**
* Standalone Admin Handler
*
* Handles /admin requests without requiring .htaccess modifications.
* Uses WordPress template_redirect hook to catch requests early.
*
* @package WooNooW\Admin
*/
class StandaloneAdmin {
/**
* Initialize standalone admin handler
*/
public static function init() {
// Catch /admin requests very early (before WordPress routing)
add_action( 'parse_request', [ __CLASS__, 'handle_admin_request' ], 1 );
}
/**
* Handle /admin requests
*/
public static function handle_admin_request() {
// Check if this is an /admin request
$request_uri = $_SERVER['REQUEST_URI'] ?? '';
// Remove query string
$path = strtok( $request_uri, '?' );
// Only handle exact /admin or /admin/ paths (not asset files)
if ( $path !== '/admin' && $path !== '/admin/' ) {
return;
}
// This is a standalone admin request
self::render_standalone_admin();
exit;
}
/**
* Render standalone admin interface
*/
private static function render_standalone_admin() {
// Check if user is logged in and has permissions
$is_logged_in = is_user_logged_in();
$has_permission = $is_logged_in && current_user_can( 'manage_woocommerce' );
$is_authenticated = $is_logged_in && $has_permission;
// Debug logging (only in WP_DEBUG mode)
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( '[StandaloneAdmin] is_user_logged_in: ' . ( $is_logged_in ? 'true' : 'false' ) );
error_log( '[StandaloneAdmin] has manage_woocommerce: ' . ( $has_permission ? 'true' : 'false' ) );
error_log( '[StandaloneAdmin] is_authenticated: ' . ( $is_authenticated ? 'true' : 'false' ) );
}
// Get nonce for REST API
$nonce = wp_create_nonce( 'wp_rest' );
$rest_url = rest_url( 'woonoow/v1' );
$wp_admin_url = admin_url( 'admin.php?page=woonoow' );
// Get current user data if authenticated
$current_user = null;
if ( $is_authenticated ) {
$user = wp_get_current_user();
$current_user = [
'id' => $user->ID,
'name' => $user->display_name,
'email' => $user->user_email,
'avatar' => get_avatar_url( $user->ID ),
];
}
// Get WooCommerce store settings
$store_settings = self::get_store_settings();
// Get asset URLs
$plugin_url = plugins_url( '', dirname( dirname( __FILE__ ) ) );
$asset_url = $plugin_url . '/admin-spa/dist';
// Cache busting
$version = defined( 'WP_DEBUG' ) && WP_DEBUG ? time() : '1.0.0';
$css_url = $asset_url . '/app.css?ver=' . $version;
$js_url = $asset_url . '/app.js?ver=' . $version;
// Render HTML
?>
<!DOCTYPE html>
<html lang="<?php echo esc_attr( get_locale() ); ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, nofollow">
<title><?php echo esc_html( get_option( 'blogname', 'WooNooW' ) ); ?> Admin</title>
<?php
// Favicon
$icon = get_option( 'woonoow_store_icon', '' );
if ( ! empty( $icon ) ) {
?>
<link rel="icon" type="image/png" href="<?php echo esc_url( $icon ); ?>" />
<link rel="apple-touch-icon" href="<?php echo esc_url( $icon ); ?>" />
<?php
}
?>
<!-- WooNooW Assets Only - NO wp_head() -->
<link rel="stylesheet" href="<?php echo esc_url( $css_url ); ?>">
</head>
<body class="woonoow-standalone">
<div id="woonoow-admin-app"></div>
<script>
// Minimal config - no WordPress bloat
window.WNW_CONFIG = {
restUrl: <?php echo wp_json_encode( $rest_url ); ?>,
nonce: <?php echo wp_json_encode( $nonce ); ?>,
standaloneMode: true,
wpAdminUrl: <?php echo wp_json_encode( $wp_admin_url ); ?>,
isAuthenticated: <?php echo $is_authenticated ? 'true' : 'false'; ?>,
currentUser: <?php echo wp_json_encode( $current_user ); ?>,
locale: <?php echo wp_json_encode( get_locale() ); ?>,
siteUrl: <?php echo wp_json_encode( home_url() ); ?>,
siteName: <?php echo wp_json_encode( get_bloginfo( 'name' ) ); ?>
};
// Also set WNW_API for API compatibility
window.WNW_API = {
root: <?php echo wp_json_encode( $rest_url ); ?>,
nonce: <?php echo wp_json_encode( $nonce ); ?>,
isDev: <?php echo ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? 'true' : 'false'; ?>
};
// WooCommerce store settings (currency, formatting, etc.)
window.WNW_STORE = <?php echo wp_json_encode( $store_settings ); ?>;
</script>
<script type="module" src="<?php echo esc_url( $js_url ); ?>"></script>
<?php
// NO wp_footer() - we don't want theme/plugin scripts
?>
</body>
</html>
<?php
}
/**
* Get WooCommerce store settings for frontend
*
* @return array Store settings (currency, decimals, separators, etc.)
*/
private static function get_store_settings(): array {
// Get WooCommerce settings with fallbacks
$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 = get_option( 'woocommerce_currency_pos', '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,
];
}
}