From 6c8cbb93e6c67358a26dca0beb6aca669a379766 Mon Sep 17 00:00:00 2001 From: Dwindi Ramadhana Date: Sun, 4 Jan 2026 23:51:54 +0700 Subject: [PATCH] feat: Add Store link to admin header and notification activity log - Add Store link to admin header (visible when customer SPA is enabled) - Add storeUrl and customerSpaEnabled to WNW_CONFIG in Assets.php and StandaloneAdmin.php - Update window.d.ts with new WNW_CONFIG properties - Create ActivityLog.tsx component with search, filters, and pagination - Add /notifications/logs API endpoint to NotificationsController - Update Notifications.tsx to link to activity log page - Add ActivityLog route to App.tsx --- admin-spa/src/App.tsx | 24 ++ admin-spa/src/routes/Orders/Detail.tsx | 49 ++-- .../src/routes/Settings/Notifications.tsx | 12 +- .../Settings/Notifications/ActivityLog.tsx | 241 ++++++++++++++++++ admin-spa/src/types/window.d.ts | 6 +- includes/Admin/Assets.php | 4 + includes/Admin/StandaloneAdmin.php | 4 +- includes/Api/NotificationsController.php | 59 +++++ 8 files changed, 366 insertions(+), 33 deletions(-) create mode 100644 admin-spa/src/routes/Settings/Notifications/ActivityLog.tsx diff --git a/admin-spa/src/App.tsx b/admin-spa/src/App.tsx index 456d81c..55aea78 100644 --- a/admin-spa/src/App.tsx +++ b/admin-spa/src/App.tsx @@ -243,6 +243,7 @@ import EmailConfiguration from '@/routes/Settings/Notifications/EmailConfigurati import PushConfiguration from '@/routes/Settings/Notifications/PushConfiguration'; import EmailCustomization from '@/routes/Settings/Notifications/EmailCustomization'; import EditTemplate from '@/routes/Settings/Notifications/EditTemplate'; +import ActivityLog from '@/routes/Settings/Notifications/ActivityLog'; import SettingsDeveloper from '@/routes/Settings/Developer'; import SettingsModules from '@/routes/Settings/Modules'; import ModuleSettings from '@/routes/Settings/ModuleSettings'; @@ -462,6 +463,17 @@ function Header({ onFullscreen, fullscreen, showToggle = true, scrollContainerRe > {__('WordPress')} + {window.WNW_CONFIG?.customerSpaEnabled && ( + + {__('Store')} + + )} - - {__('Orders')} - @@ -232,8 +229,8 @@ export default function OrderShow() {
{__('Summary')}
- setSearch(e.target.value)} + className="!pl-9" + /> +
+
+ + + + + + + + {/* Activity Log Table */} + + + {__('Recent Activity')} + + {data?.total ? `${data.total} ${__('notifications found')}` : __('Loading...')} + + + + {isLoading ? ( +
+ +

{__('Loading activity log...')}

+
+ ) : error ? ( +
+ +

{__('Failed to load activity log')}

+ +
+ ) : !data?.logs?.length ? ( +
+ +

{__('No notifications yet')}

+

{__('Notification activities will appear here once sent.')}

+
+ ) : ( +
+ {data.logs.map((log) => ( +
+ {/* Channel Icon */} +
+ {channelIcons[log.channel] || } +
+ + {/* Content */} +
+
+ {log.event} + + {statusConfig[log.status]?.icon} + {statusConfig[log.status]?.label || log.status} + +
+

+ {__('To')}: {log.recipient} + {log.subject && ` — ${log.subject}`} +

+ {log.error_message && ( +

+ {__('Error')}: {log.error_message} +

+ )} +
+ + {/* Timestamp */} +
+ {formatDate(log.sent_at || log.created_at)} +
+
+ ))} + + {/* Pagination */} + {data.total > 20 && ( +
+ + + {__('Page')} {page} {__('of')} {Math.ceil(data.total / 20)} + + +
+ )} +
+ )} +
+
+ + + ); +} diff --git a/admin-spa/src/types/window.d.ts b/admin-spa/src/types/window.d.ts index 2bc5e81..6ee9e1d 100644 --- a/admin-spa/src/types/window.d.ts +++ b/admin-spa/src/types/window.d.ts @@ -41,6 +41,10 @@ interface WNW_CONFIG { decimalSeparator: string; decimals: number; }; + storeUrl?: string; + customerSpaEnabled?: boolean; + nonce?: string; + pluginUrl?: string; } declare global { @@ -52,4 +56,4 @@ declare global { } } -export {}; +export { }; diff --git a/includes/Admin/Assets.php b/includes/Admin/Assets.php index 2452643..5b2fc3f 100644 --- a/includes/Admin/Assets.php +++ b/includes/Admin/Assets.php @@ -72,6 +72,8 @@ class Assets 'wpAdminUrl' => admin_url('admin.php?page=woonoow'), 'isAuthenticated' => is_user_logged_in(), 'pluginUrl' => trailingslashit(plugins_url('/', dirname(__DIR__))), + 'storeUrl' => home_url('/store/'), + 'customerSpaEnabled' => get_option('woonoow_customer_spa_enabled', false), ]); wp_add_inline_script($handle, 'window.WNW_CONFIG = window.WNW_CONFIG || WNW_CONFIG;', 'after'); @@ -195,6 +197,8 @@ class Assets 'wpAdminUrl' => admin_url('admin.php?page=woonoow'), 'isAuthenticated' => is_user_logged_in(), 'pluginUrl' => trailingslashit(plugins_url('/', dirname(__DIR__))), + 'storeUrl' => home_url('/store/'), + 'customerSpaEnabled' => get_option('woonoow_customer_spa_enabled', false), ]); // WordPress REST API settings (for media upload compatibility) diff --git a/includes/Admin/StandaloneAdmin.php b/includes/Admin/StandaloneAdmin.php index 6f944dc..889d1e7 100644 --- a/includes/Admin/StandaloneAdmin.php +++ b/includes/Admin/StandaloneAdmin.php @@ -132,7 +132,9 @@ class StandaloneAdmin { currentUser: , locale: , siteUrl: , - siteName: + siteName: , + storeUrl: , + customerSpaEnabled: }; // Also set WNW_API for API compatibility diff --git a/includes/Api/NotificationsController.php b/includes/Api/NotificationsController.php index d2ff524..dc02e1a 100644 --- a/includes/Api/NotificationsController.php +++ b/includes/Api/NotificationsController.php @@ -217,6 +217,15 @@ class NotificationsController { 'permission_callback' => [$this, 'check_permission'], ], ]); + + // GET /woonoow/v1/notifications/logs + register_rest_route($this->namespace, '/' . $this->rest_base . '/logs', [ + [ + 'methods' => 'GET', + 'callback' => [$this, 'get_logs'], + 'permission_callback' => [$this, 'check_permission'], + ], + ]); } /** @@ -872,4 +881,54 @@ class NotificationsController { ), ], 200); } + + /** + * Get notification activity logs + * + * @param WP_REST_Request $request Request object + * @return WP_REST_Response + */ + public function get_logs(WP_REST_Request $request) { + $page = (int) $request->get_param('page') ?: 1; + $per_page = (int) $request->get_param('per_page') ?: 20; + $channel = $request->get_param('channel'); + $status = $request->get_param('status'); + $search = $request->get_param('search'); + + // Get logs from option (in a real app, use a custom table) + $all_logs = get_option('woonoow_notification_logs', []); + + // Apply filters + if ($channel && $channel !== 'all') { + $all_logs = array_filter($all_logs, fn($log) => $log['channel'] === $channel); + } + + if ($status && $status !== 'all') { + $all_logs = array_filter($all_logs, fn($log) => $log['status'] === $status); + } + + if ($search) { + $search_lower = strtolower($search); + $all_logs = array_filter($all_logs, function($log) use ($search_lower) { + return strpos(strtolower($log['recipient'] ?? ''), $search_lower) !== false || + strpos(strtolower($log['subject'] ?? ''), $search_lower) !== false; + }); + } + + // Sort by date descending + usort($all_logs, function($a, $b) { + return strtotime($b['created_at'] ?? '') - strtotime($a['created_at'] ?? ''); + }); + + $total = count($all_logs); + $offset = ($page - 1) * $per_page; + $logs = array_slice(array_values($all_logs), $offset, $per_page); + + return new WP_REST_Response([ + 'logs' => $logs, + 'total' => $total, + 'page' => $page, + 'per_page' => $per_page, + ], 200); + } }