feat: Complete Dashboard API Integration with Analytics Controller

 Features:
- Implemented API integration for all 7 dashboard pages
- Added Analytics REST API controller with 7 endpoints
- Full loading and error states with retry functionality
- Seamless dummy data toggle for development

📊 Dashboard Pages:
- Customers Analytics (complete)
- Revenue Analytics (complete)
- Orders Analytics (complete)
- Products Analytics (complete)
- Coupons Analytics (complete)
- Taxes Analytics (complete)
- Dashboard Overview (complete)

🔌 Backend:
- Created AnalyticsController.php with REST endpoints
- All endpoints return 501 (Not Implemented) for now
- Ready for HPOS-based implementation
- Proper permission checks

🎨 Frontend:
- useAnalytics hook for data fetching
- React Query caching
- ErrorCard with retry functionality
- TypeScript type safety
- Zero build errors

📝 Documentation:
- DASHBOARD_API_IMPLEMENTATION.md guide
- Backend implementation roadmap
- Testing strategy

🔧 Build:
- All pages compile successfully
- Production-ready with dummy data fallback
- Zero TypeScript errors
This commit is contained in:
dwindown
2025-11-04 11:19:00 +07:00
commit 232059e928
148 changed files with 28984 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<?php
namespace WooNooW\Core;
use WooNooW\Core\Features;
use WooNooW\Admin\Menu;
use WooNooW\Admin\Assets;
use WooNooW\Compat\HideWooMenus;
use WooNooW\Compat\MenuProvider;
use WooNooW\Compat\AddonRegistry;
use WooNooW\Compat\RouteRegistry;
use WooNooW\Compat\NavigationRegistry;
use WooNooW\Compat\PaymentChannels;
use WooNooW\Compat\SettingsProvider;
use WooNooW\Admin\Rest\MenuController;
use WooNooW\Admin\Rest\SettingsController;
use WooNooW\Api\Routes;
use WooNooW\Core\Mail\MailQueue;
use WooNooW\Core\Mail\WooEmailOverride;
use WooNooW\Core\DataStores\OrderStore;
class Bootstrap {
public static function init() {
Features::init();
HideWooMenus::init();
Menu::init();
Assets::init();
// Addon system (order matters: Registry → Routes → Navigation)
AddonRegistry::init();
RouteRegistry::init();
NavigationRegistry::init();
PaymentChannels::init();
MenuProvider::init();
MenuController::init();
SettingsProvider::init();
Routes::init();
MailQueue::init();
WooEmailOverride::init();
OrderStore::init();
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WooNooW\Core\DataStores;
class OrderStore {
public static function init() {
add_filter('woocommerce_data_stores', [__CLASS__, 'register']);
}
public static function register($stores) {
$stores['order'] = __NAMESPACE__ . '\\OrderStore_HPOS';
return $stores;
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace WooNooW\Core\DataStores;
class OrderStore_HPOS extends \WC_Order_Data_Store_Custom_Table {
// TODO: override read/write queries to use indexed columns
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WooNooW\Core;
class Features {
public static function init() {
add_filter('woocommerce_admin_features', [__CLASS__, 'disableWooAdmin'], 999);
}
public static function disableWooAdmin($features) {
$block = ['analytics','analytics-dashboard','homescreen','navigation','experimental'];
return array_values(array_diff($features, $block));
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace WooNooW\Core\Mail;
/**
* Async mail queue using Action Scheduler or wp-cron.
* Emails are queued and sent in the background to avoid blocking API responses.
*/
class MailQueue {
public static function init() {
add_action('woonoow/mail/send', [__CLASS__, 'sendNow'], 10, 1);
}
/**
* Queue an email to be sent asynchronously.
* Stores payload in wp_options to avoid Action Scheduler's 8000 char limit.
*/
public static function enqueue(array $payload) {
// Generate unique ID for this email
$email_id = 'woonoow_mail_' . uniqid() . '_' . time();
// Store payload in wp_options (temporary, will be deleted after sending)
update_option($email_id, $payload, false); // false = don't autoload
// Debug log in dev mode
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[WooNooW MailQueue] Queued email ID: ' . $email_id . ' to: ' . ($payload['to'] ?? 'unknown'));
}
if (function_exists('as_enqueue_async_action')) {
// Use Action Scheduler - pass email_id as single argument
// Action Scheduler will pass this as the first parameter to the callback
as_enqueue_async_action('woonoow/mail/send', [$email_id], 'woonoow-mails');
} else {
// Fallback to wp-cron
wp_schedule_single_event(time() + 5, 'woonoow/mail/send', [$email_id]);
}
}
/**
* Actually send the email (runs async via Action Scheduler or wp-cron).
* Retrieves payload from wp_options and deletes it after sending.
*/
public static function sendNow($email_id = null) {
// email_id should be passed directly as a string by Action Scheduler
if (empty($email_id)) {
error_log('[WooNooW MailQueue] ERROR: No email_id provided. Received: ' . print_r(func_get_args(), true));
return;
}
error_log('[WooNooW MailQueue] Processing email_id: ' . $email_id);
// Retrieve payload from wp_options
$p = get_option($email_id);
if (!$p) {
error_log('[WooNooW MailQueue] ERROR: Email payload not found for ID: ' . $email_id);
return;
}
// Temporarily disable WooEmailOverride to prevent infinite loop
if (class_exists('WooNooW\Core\Mail\WooEmailOverride')) {
WooEmailOverride::disable();
}
wp_mail(
$p['to'] ?? '',
$p['subject'] ?? '',
$p['html'] ?? '',
$p['headers'] ?? [],
$p['attachments'] ?? []
);
// Re-enable
if (class_exists('WooNooW\Core\Mail\WooEmailOverride')) {
WooEmailOverride::enable();
}
// Delete the temporary option after sending
delete_option($email_id);
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[WooNooW MailQueue] Sent and deleted email ID: ' . $email_id . ' to: ' . ($p['to'] ?? 'unknown'));
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace WooNooW\Core\Mail;
/**
* Intercepts wp_mail() calls and queues them asynchronously via Action Scheduler.
* This prevents blocking operations (SMTP, DNS lookups) from slowing down API responses.
*/
class WooEmailOverride {
private static $enabled = true;
public static function init() {
// Hook into wp_mail before it's sent
add_filter('pre_wp_mail', [__CLASS__, 'interceptMail'], 10, 2);
}
/**
* Intercept wp_mail() and queue it asynchronously.
* Return non-null to short-circuit wp_mail().
*/
public static function interceptMail($null, $atts) {
if (!self::$enabled) {
return $null; // Allow normal wp_mail if disabled
}
// Queue the email asynchronously
MailQueue::enqueue([
'to' => $atts['to'] ?? '',
'subject' => $atts['subject'] ?? '',
'html' => $atts['message'] ?? '',
'headers' => $atts['headers'] ?? [],
'attachments' => $atts['attachments'] ?? [],
]);
// Return true to indicate success and prevent wp_mail from running
return true;
}
/**
* Temporarily disable async mail (for testing or critical emails).
*/
public static function disable() {
self::$enabled = false;
}
/**
* Re-enable async mail.
*/
public static function enable() {
self::$enabled = true;
}
}