Navigation Fixes:
1. Newsletter submenu now hidden when module disabled
- NavigationRegistry checks ModuleRegistry::is_enabled('newsletter')
- Menu updates dynamically based on module status
2. Module toggle now updates navigation in real-time
- Fixed toggle_module API to return success response (was returning error)
- Navigation cache flushes and rebuilds when module toggled
- Newsletter menu appears/disappears immediately after toggle
3. Coupon routes now activate Marketing menu (not Dashboard)
- Added special case in useActiveSection for /coupons paths
- Marketing menu stays active when viewing coupons
- Submenu shows correct Marketing items (Newsletter, Coupons)
4. Dashboard menu no longer always shows active
- Fixed by proper path matching in useActiveSection
- Only active when on dashboard routes
Files Modified (4):
- includes/Compat/NavigationRegistry.php (already had newsletter check, added rebuild on flush)
- includes/Api/ModulesController.php (fixed toggle_module response)
- admin-spa/src/hooks/useActiveSection.ts (added /coupons special case)
- admin-spa/dist/app.js (rebuilt)
All 4 navigation issues resolved!
280 lines
11 KiB
PHP
280 lines
11 KiB
PHP
<?php
|
|
namespace WooNooW\Compat;
|
|
|
|
if ( ! defined('ABSPATH') ) exit;
|
|
|
|
/**
|
|
* Navigation Registry
|
|
*
|
|
* Manages dynamic navigation tree building. Allows addons to inject
|
|
* menu items into the main navigation or existing sections.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class NavigationRegistry {
|
|
const NAV_OPTION = 'wnw_nav_tree';
|
|
const NAV_VERSION = '1.0.8'; // Added Modules to Settings menu
|
|
|
|
/**
|
|
* Initialize hooks
|
|
*/
|
|
public static function init() {
|
|
// Use 'init' hook instead of 'plugins_loaded' to avoid translation loading warnings (WP 6.7+)
|
|
add_action('init', [__CLASS__, 'build_nav_tree'], 10);
|
|
add_action('activated_plugin', [__CLASS__, 'flush']);
|
|
add_action('deactivated_plugin', [__CLASS__, 'flush']);
|
|
}
|
|
|
|
/**
|
|
* Build the complete navigation tree
|
|
*/
|
|
public static function build_nav_tree() {
|
|
// Check if we need to rebuild (version mismatch)
|
|
$cached = get_option(self::NAV_OPTION, []);
|
|
$cached_version = $cached['version'] ?? '';
|
|
|
|
if ($cached_version === self::NAV_VERSION && !empty($cached['tree'])) {
|
|
// Cache is valid, no need to rebuild
|
|
return;
|
|
}
|
|
|
|
// Base navigation tree (core WooNooW sections)
|
|
$tree = self::get_base_tree();
|
|
|
|
/**
|
|
* Filter: woonoow/nav_tree
|
|
*
|
|
* Allows addons to modify the entire navigation tree.
|
|
*
|
|
* @param array $tree Array of main navigation nodes
|
|
*
|
|
* Example:
|
|
* add_filter('woonoow/nav_tree', function($tree) {
|
|
* $tree[] = [
|
|
* 'key' => 'subscriptions',
|
|
* 'label' => 'Subscriptions',
|
|
* 'path' => '/subscriptions',
|
|
* 'icon' => 'repeat', // lucide icon name
|
|
* 'children' => [
|
|
* ['label' => 'All Subscriptions', 'mode' => 'spa', 'path' => '/subscriptions'],
|
|
* ['label' => 'New', 'mode' => 'spa', 'path' => '/subscriptions/new'],
|
|
* ],
|
|
* ];
|
|
* return $tree;
|
|
* });
|
|
*/
|
|
$tree = apply_filters('woonoow/nav_tree', $tree);
|
|
|
|
// Allow per-section modification
|
|
foreach ($tree as &$section) {
|
|
$key = $section['key'] ?? '';
|
|
if ($key) {
|
|
/**
|
|
* Filter: woonoow/nav_tree/{key}/children
|
|
*
|
|
* Allows addons to inject items into specific sections.
|
|
*
|
|
* Example:
|
|
* add_filter('woonoow/nav_tree/products/children', function($children) {
|
|
* $children[] = [
|
|
* 'label' => 'Bundles',
|
|
* 'mode' => 'spa',
|
|
* 'path' => '/products/bundles',
|
|
* ];
|
|
* return $children;
|
|
* });
|
|
*/
|
|
$section['children'] = apply_filters(
|
|
"woonoow/nav_tree/{$key}/children",
|
|
$section['children'] ?? []
|
|
);
|
|
}
|
|
}
|
|
|
|
// Store in option
|
|
update_option(self::NAV_OPTION, [
|
|
'version' => self::NAV_VERSION,
|
|
'tree' => $tree,
|
|
'updated' => time(),
|
|
], false);
|
|
}
|
|
|
|
/**
|
|
* Get base navigation tree (core sections)
|
|
*
|
|
* @return array Base navigation tree
|
|
*/
|
|
private static function get_base_tree(): array {
|
|
$tree = [
|
|
[
|
|
'key' => 'dashboard',
|
|
'label' => __('Dashboard', 'woonoow'),
|
|
'path' => '/',
|
|
'icon' => 'layout-dashboard',
|
|
'children' => [
|
|
['label' => __('Overview', 'woonoow'), 'mode' => 'spa', 'path' => '/', 'exact' => true],
|
|
['label' => __('Revenue', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/revenue'],
|
|
['label' => __('Orders', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/orders'],
|
|
['label' => __('Products', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/products'],
|
|
['label' => __('Customers', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/customers'],
|
|
['label' => __('Coupons', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/coupons'],
|
|
['label' => __('Taxes', 'woonoow'), 'mode' => 'spa', 'path' => '/dashboard/taxes'],
|
|
],
|
|
],
|
|
[
|
|
'key' => 'orders',
|
|
'label' => __('Orders', 'woonoow'),
|
|
'path' => '/orders',
|
|
'icon' => 'receipt-text',
|
|
'children' => [
|
|
['label' => __('All orders', 'woonoow'), 'mode' => 'spa', 'path' => '/orders'],
|
|
['label' => __('New', 'woonoow'), 'mode' => 'spa', 'path' => '/orders/new'],
|
|
// Future: Drafts, Recurring, etc.
|
|
],
|
|
],
|
|
[
|
|
'key' => 'products',
|
|
'label' => __('Products', 'woonoow'),
|
|
'path' => '/products',
|
|
'icon' => 'package',
|
|
'children' => [
|
|
['label' => __('All products', 'woonoow'), 'mode' => 'spa', 'path' => '/products'],
|
|
['label' => __('New', 'woonoow'), 'mode' => 'spa', 'path' => '/products/new'],
|
|
['label' => __('Categories', 'woonoow'), 'mode' => 'spa', 'path' => '/products/categories'],
|
|
['label' => __('Tags', 'woonoow'), 'mode' => 'spa', 'path' => '/products/tags'],
|
|
['label' => __('Attributes', 'woonoow'), 'mode' => 'spa', 'path' => '/products/attributes'],
|
|
],
|
|
],
|
|
[
|
|
'key' => 'customers',
|
|
'label' => __('Customers', 'woonoow'),
|
|
'path' => '/customers',
|
|
'icon' => 'users',
|
|
'children' => [
|
|
['label' => __('All customers', 'woonoow'), 'mode' => 'spa', 'path' => '/customers'],
|
|
['label' => __('New', 'woonoow'), 'mode' => 'spa', 'path' => '/customers/new'],
|
|
],
|
|
],
|
|
[
|
|
'key' => 'marketing',
|
|
'label' => __('Marketing', 'woonoow'),
|
|
'path' => '/marketing',
|
|
'icon' => 'mail',
|
|
'children' => self::get_marketing_children(),
|
|
],
|
|
[
|
|
'key' => 'appearance',
|
|
'label' => __('Appearance', 'woonoow'),
|
|
'path' => '/appearance',
|
|
'icon' => 'palette',
|
|
'children' => [
|
|
['label' => __('General', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/general'],
|
|
['label' => __('Header', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/header'],
|
|
['label' => __('Footer', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/footer'],
|
|
['label' => __('Shop', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/shop'],
|
|
['label' => __('Product', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/product'],
|
|
['label' => __('Cart', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/cart'],
|
|
['label' => __('Checkout', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/checkout'],
|
|
['label' => __('Thank You', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/thankyou'],
|
|
['label' => __('My Account', 'woonoow'), 'mode' => 'spa', 'path' => '/appearance/account'],
|
|
],
|
|
],
|
|
[
|
|
'key' => 'settings',
|
|
'label' => __('Settings', 'woonoow'),
|
|
'path' => '/settings',
|
|
'icon' => 'settings',
|
|
'children' => self::get_settings_children(),
|
|
],
|
|
];
|
|
|
|
return $tree;
|
|
}
|
|
|
|
/**
|
|
* Get marketing submenu children
|
|
*
|
|
* @return array Marketing submenu items
|
|
*/
|
|
private static function get_marketing_children(): array {
|
|
$children = [];
|
|
|
|
// Newsletter - only if module enabled
|
|
if (\WooNooW\Core\ModuleRegistry::is_enabled('newsletter')) {
|
|
$children[] = ['label' => __('Newsletter', 'woonoow'), 'mode' => 'spa', 'path' => '/marketing/newsletter'];
|
|
}
|
|
|
|
// Coupons - always available
|
|
$children[] = ['label' => __('Coupons', 'woonoow'), 'mode' => 'spa', 'path' => '/coupons'];
|
|
|
|
return $children;
|
|
}
|
|
|
|
/**
|
|
* Get settings submenu children
|
|
*
|
|
* @return array Settings submenu items
|
|
*/
|
|
private static function get_settings_children(): array {
|
|
$admin = admin_url('admin.php');
|
|
|
|
$children = [
|
|
// Core Settings (Shopify-inspired)
|
|
['label' => __('Store Details', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/store'],
|
|
['label' => __('Payments', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/payments'],
|
|
['label' => __('Shipping & Delivery', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/shipping'],
|
|
['label' => __('Tax', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/tax'],
|
|
['label' => __('Customers', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/customers'],
|
|
['label' => __('Notifications', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/notifications'],
|
|
['label' => __('Modules', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/modules'],
|
|
['label' => __('Developer', 'woonoow'), 'mode' => 'spa', 'path' => '/settings/developer'],
|
|
];
|
|
|
|
return $children;
|
|
}
|
|
|
|
/**
|
|
* Get the complete navigation tree
|
|
*
|
|
* @return array Navigation tree
|
|
*/
|
|
public static function get_nav_tree(): array {
|
|
$data = get_option(self::NAV_OPTION, []);
|
|
return $data['tree'] ?? self::get_base_tree();
|
|
}
|
|
|
|
/**
|
|
* Get a specific section by key
|
|
*
|
|
* @param string $key Section key
|
|
* @return array|null Section data or null if not found
|
|
*/
|
|
public static function get_section(string $key): ?array {
|
|
$tree = self::get_nav_tree();
|
|
foreach ($tree as $section) {
|
|
if (($section['key'] ?? '') === $key) {
|
|
return $section;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Flush navigation cache
|
|
*/
|
|
public static function flush() {
|
|
delete_option(self::NAV_OPTION);
|
|
// Rebuild immediately after flush
|
|
self::build_nav_tree();
|
|
}
|
|
|
|
/**
|
|
* Get navigation tree for frontend
|
|
*
|
|
* @return array Array suitable for JSON encoding
|
|
*/
|
|
public static function get_frontend_nav_tree(): array {
|
|
return self::get_nav_tree();
|
|
}
|
|
}
|