settings = $settings ?? new Settings(); $this->database = $database ?? new Database(); $this->metabox_manager = new Metabox_Manager($this->settings); } /** * Initialize admin interface * * @return void */ public function init(): void { add_action('admin_menu', [$this, 'add_admin_menu']); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); add_action('admin_init', [$this, 'handle_admin_init']); add_action('admin_notices', [$this, 'show_admin_notices']); // AJAX handlers add_action('wp_ajax_thinkrank_dismiss_notice', [$this, 'dismiss_notice']); // Initialize metabox manager $this->metabox_manager->init(); } /** * Add admin menu pages * * @return void */ public function add_admin_menu(): void { // Main menu page $this->pages['dashboard'] = add_menu_page( __('ThinkRank', 'thinkrank'), __('ThinkRank', 'thinkrank'), 'manage_options', 'thinkrank', [$this, 'render_dashboard_page'], $this->get_menu_icon(), 30 ); // Dashboard submenu (same as main) $this->pages['dashboard_sub'] = add_submenu_page( 'thinkrank', __('Dashboard', 'thinkrank'), __('Dashboard', 'thinkrank'), 'manage_options', 'thinkrank', [$this, 'render_dashboard_page'] ); // Essential SEO page (React tabbed interface) $this->pages['essential_seo'] = add_submenu_page( 'thinkrank', __('Essential SEO', 'thinkrank'), __('Essential SEO', 'thinkrank'), 'manage_options', 'thinkrank-essential-seo', [$this, 'render_essential_seo_page'] ); // AI Tools page $this->pages['ai_tools'] = add_submenu_page( 'thinkrank', __('AI Tools', 'thinkrank'), __('AI Tools', 'thinkrank'), 'edit_posts', 'thinkrank-ai-tools', [$this, 'render_ai_tools_page'] ); // Settings page $this->pages['settings'] = add_submenu_page( 'thinkrank', __('Settings', 'thinkrank'), __('Settings', 'thinkrank'), 'manage_options', 'thinkrank-settings', [$this, 'render_settings_page'] ); // Usage Analytics page $this->pages['analytics'] = add_submenu_page( 'thinkrank', __('Usage Analytics', 'thinkrank'), __('Usage Analytics', 'thinkrank'), 'edit_posts', 'thinkrank-analytics', [$this, 'render_analytics_page'] ); // Hook for page-specific initialization foreach ($this->pages as $page_hook) { add_action("load-{$page_hook}", [$this, 'load_admin_page']); } } /** * Enqueue admin scripts and styles * * APPROACH: Manual enqueuing with disabled webpack code splitting * - All dependencies bundled into main admin.js (677KB) * - Chart.js separated into charts.js (138KB) for performance * - No dynamic chunks - predictable loading order * * @param string $hook_suffix Current admin page hook * @return void */ public function enqueue_admin_scripts(string $hook_suffix): void { // Only load on our admin pages if (!in_array($hook_suffix, $this->pages, true)) { return; } // Get asset files for cache busting $admin_asset_file = THINKRANK_PLUGIN_DIR . 'assets/admin.asset.php'; $admin_asset_data = file_exists($admin_asset_file) ? include $admin_asset_file : [ 'dependencies' => [], 'version' => THINKRANK_VERSION, ]; // Enqueue Chart.js bundle only on pages that render charts (Analytics and Essential SEO Performance) $charts_asset_file = THINKRANK_PLUGIN_DIR . 'assets/charts.asset.php'; $should_enqueue_charts = in_array($hook_suffix, [ $this->pages['analytics'] ?? '', $this->pages['essential_seo'] ?? '', ], true); if ($should_enqueue_charts && file_exists($charts_asset_file)) { $charts_asset_data = include $charts_asset_file; wp_enqueue_script( 'thinkrank-charts', THINKRANK_PLUGIN_URL . 'assets/charts.js', $charts_asset_data['dependencies'], $charts_asset_data['version'], true ); // Add charts as dependency for admin script to ensure registration before use $admin_dependencies = array_merge($admin_asset_data['dependencies'], ['thinkrank-charts']); } else { // Do not load charts on pages that don't need it $admin_dependencies = $admin_asset_data['dependencies']; } wp_enqueue_script( 'thinkrank-admin', THINKRANK_PLUGIN_URL . 'assets/admin.js', $admin_dependencies, $admin_asset_data['version'], true ); // Add defer attribute for better performance wp_script_add_data('thinkrank-admin', 'defer', true); // Enqueue admin styles wp_enqueue_style( 'thinkrank-admin', THINKRANK_PLUGIN_URL . 'assets/admin.css', ['wp-components'], $admin_asset_data['version'] ); // Enqueue WordPress media library for MediaPicker component wp_enqueue_media(); // Localize script with data wp_localize_script('thinkrank-admin', 'thinkrankAdmin', [ 'apiUrl' => rest_url('thinkrank/v1/'), 'nonce' => wp_create_nonce('wp_rest'), 'restNonce' => wp_create_nonce('wp_rest'), 'adminNonce' => wp_create_nonce('thinkrank_admin'), 'currentUser' => wp_get_current_user()->ID, 'capabilities' => $this->get_user_capabilities(), 'settings' => $this->get_admin_settings(), 'i18n' => $this->get_i18n_strings(), 'isAdmin' => current_user_can('manage_options'), // Site information for default values 'siteName' => get_bloginfo('name'), 'siteDescription' => get_bloginfo('description'), 'siteUrl' => home_url(), 'adminEmail' => get_option('admin_email'), ]); // Add welcome notice dismissal script if (get_option('thinkrank_show_welcome') && !$this->has_api_key_configured()) { wp_add_inline_script('thinkrank-admin', ' jQuery(document).ready(function($) { $(".thinkrank-dismiss-welcome").on("click", function(e) { e.preventDefault(); $.post(ajaxurl, { action: "thinkrank_dismiss_notice", notice_type: "welcome", nonce: thinkrankAdmin.adminNonce }, function(response) { $(".thinkrank-welcome-notice").fadeOut(); }); }); }); '); } } /** * Handle admin initialization * * @return void */ public function handle_admin_init(): void { // Show welcome screen for new installations (only if no API key configured) if (get_option('thinkrank_show_welcome') && !$this->has_api_key_configured()) { add_action('admin_notices', [$this, 'show_welcome_notice']); } // Check for plugin updates $this->check_plugin_updates(); } /** * Load admin page * * @return void */ public function load_admin_page(): void { // Add help tabs $this->add_help_tabs(); // Add screen options $this->add_screen_options(); } /** * Render dashboard page * * @return void */ public function render_dashboard_page(): void { $this->render_admin_page('dashboard', [ 'title' => __('ThinkRank Dashboard', 'thinkrank'), 'description' => __('AI-powered SEO optimization for WordPress', 'thinkrank'), ]); } /** * Render Essential SEO page * * @return void */ public function render_essential_seo_page(): void { $this->render_admin_page('essential-seo', [ 'title' => __('Essential SEO', 'thinkrank'), 'description' => __('Configure your site-wide SEO settings with AI-powered optimization', 'thinkrank'), ]); } /** * Render AI Tools page * * @return void */ public function render_ai_tools_page(): void { $this->render_admin_page('ai-tools', [ 'title' => __('AI Tools', 'thinkrank'), 'description' => __('AI-powered content tools including Content Planner and Metadata Generator', 'thinkrank'), ]); } /** * Render settings page * * @return void */ public function render_settings_page(): void { $this->render_admin_page('settings', [ 'title' => __('ThinkRank Settings', 'thinkrank'), 'description' => __('Configure your AI SEO settings', 'thinkrank'), ]); } /** * Render usage analytics page * * @return void */ public function render_analytics_page(): void { $this->render_admin_page('analytics', [ 'title' => __('Usage Analytics', 'thinkrank'), 'description' => __('Track your AI usage, costs, and plugin performance analytics', 'thinkrank'), ]); } /** * Render admin page template * * @param string $page Page identifier * @param array $data Page data * @return void */ private function render_admin_page(string $page, array $data): void { ?>
' . __('ThinkRank.ai helps you optimize your content with AI-powered SEO suggestions.', 'thinkrank') . '
', ]); $screen->set_help_sidebar( '' . __('For more information:', 'thinkrank') . '
' . '' . '' ); } /** * Add screen options * * @return void */ private function add_screen_options(): void { // Screen options will be added as needed } /** * Check for plugin updates * * @return void */ private function check_plugin_updates(): void { $current_version = get_option('thinkrank_version'); if (version_compare($current_version, THINKRANK_VERSION, '<')) { // Handle plugin update do_action('thinkrank_plugin_updated', $current_version, THINKRANK_VERSION); update_option('thinkrank_version', THINKRANK_VERSION); } } /** * Get admin styles * * @return string CSS styles */ private function get_admin_styles(): string { return ' .thinkrank-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; text-align: center; } .thinkrank-loading .spinner { margin-bottom: 16px; } .thinkrank-loading p { margin: 0; color: #666; font-size: 14px; } .thinkrank-app { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; } .thinkrank-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 0; border-bottom: 1px solid #ddd; margin-bottom: 20px; } .thinkrank-header h1 { margin: 0; font-size: 24px; font-weight: 600; } .thinkrank-header .version { font-size: 12px; color: #666; font-weight: normal; margin-left: 8px; } .thinkrank-header .tagline { margin: 4px 0 0 0; color: #666; font-size: 14px; } .thinkrank-dashboard-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 20px; } .stat-card { background: #f9f9f9; padding: 20px; border-radius: 8px; text-align: center; border: 1px solid #ddd; } .stat-card h3 { margin: 0 0 10px 0; font-size: 14px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; } .stat-number { font-size: 32px; font-weight: 700; color: #0073aa; margin: 0; line-height: 1; } .stat-label { margin: 4px 0 0 0; font-size: 12px; color: #666; } .thinkrank-error { padding: 20px; } .thinkrank-error .notice { margin: 0; } '; } }