From 327cdc08d9d585ccb3a24e2d2619633f7718aef7 Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Mon, 20 Apr 2026 00:34:31 +0700 Subject: [PATCH] feat: add Ideas browser admin page (v0.5.1) New submenu page showing all article ideas from the content analyzer. Displays title, type, relevance score, frequency, key points, and keywords with type filtering and pagination. Gives live visibility into the idea pipeline. Co-Authored-By: Claude Opus 4.6 --- ARCHITECTURE.md | 1 + CHANGELOG.md | 9 ++ includes/admin/class-admin-page.php | 1 + includes/admin/class-ideas-browser.php | 135 ++++++++++++++++++++++ includes/class-prautoblogger.php | 1 + prautoblogger.php | 4 +- templates/admin/ideas-browser.php | 153 +++++++++++++++++++++++++ 7 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 includes/admin/class-ideas-browser.php create mode 100644 templates/admin/ideas-browser.php diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 88d8fd7..38cc3d1 100755 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -121,6 +121,7 @@ prautoblogger/ │ │ ├── class-dashboard-widget.php # WP Dashboard widget showing generation status │ │ ├── class-post-metabox.php # Metabox on posts showing generation metadata │ │ ├── class-metrics-page.php # Admin page for cost dashboard and content scores +│ │ ├── class-ideas-browser.php # Browse all analysis results (article ideas) │ │ ├── class-review-queue.php # Approve/edit/reject queue for generated drafts │ │ └── class-log-viewer.php # Activity log viewer with level filtering │ │ diff --git a/CHANGELOG.md b/CHANGELOG.md index 27332f0..cd44cd5 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to PRAutoBlogger will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project uses [Semantic Versioning](https://semver.org/). +## [0.5.1] — 2026-04-20 + +### Added +- **Ideas browser admin page.** New "Ideas" submenu under PRAutoBlogger showing + all article ideas collected by the content analyzer. Displays suggested title, + topic, type (question/complaint/comparison/news/guide), relevance score bar, + frequency, key points, and target keywords. Filterable by type with count + badges. Paginated at 30 per page, newest first. + ## [0.5.0] — 2026-04-20 ### Added diff --git a/includes/admin/class-admin-page.php b/includes/admin/class-admin-page.php index 728f478..f6f319e 100755 --- a/includes/admin/class-admin-page.php +++ b/includes/admin/class-admin-page.php @@ -58,6 +58,7 @@ class PRAutoBlogger_Admin_Page { 'toplevel_page_' . self::PAGE_SLUG, 'prautoblogger_page_prautoblogger-metrics', 'prautoblogger_page_prautoblogger-review-queue', + 'prautoblogger_page_prautoblogger-ideas', 'prautoblogger_page_prautoblogger-logs', ]; if ( ! in_array( $hook_suffix, $pages, true ) ) { diff --git a/includes/admin/class-ideas-browser.php b/includes/admin/class-ideas-browser.php new file mode 100644 index 0000000..e80e87e --- /dev/null +++ b/includes/admin/class-ideas-browser.php @@ -0,0 +1,135 @@ +query_ideas( $paged, $type_filter, $source_filter ); + $rows = $result['rows']; + $total = $result['total']; + $total_pages = (int) ceil( $total / self::PER_PAGE ); + $type_counts = $this->get_type_counts(); + + include PRAUTOBLOGGER_PLUGIN_DIR . 'templates/admin/ideas-browser.php'; + } + + /** + * Query analysis results with optional filtering and pagination. + * + * Side effects: database SELECT. + * + * @param int $paged Page number (1-based). + * @param string $type Filter by analysis_type (empty = all). + * @param string $source Filter: 'llm_research' or 'reddit' based on source_ids content. + * @return array{rows: array[], total: int} + */ + private function query_ideas( int $paged, string $type, string $source ): array { + global $wpdb; + $table = $wpdb->prefix . 'prautoblogger_analysis_results'; + $where = []; + $params = []; + + if ( '' !== $type ) { + $where[] = 'analysis_type = %s'; + $params[] = $type; + } + + $where_sql = empty( $where ) ? '' : 'WHERE ' . implode( ' AND ', $where ); + $offset = ( $paged - 1 ) * self::PER_PAGE; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $total = (int) $wpdb->get_var( + empty( $params ) + ? "SELECT COUNT(*) FROM {$table} {$where_sql}" + : $wpdb->prepare( "SELECT COUNT(*) FROM {$table} {$where_sql}", ...$params ) + ); + + $order_sql = 'ORDER BY analyzed_at DESC, relevance_score DESC'; + $limit_sql = sprintf( 'LIMIT %d OFFSET %d', self::PER_PAGE, $offset ); + $full_sql = "SELECT * FROM {$table} {$where_sql} {$order_sql} {$limit_sql}"; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $rows = empty( $params ) + ? $wpdb->get_results( $full_sql, ARRAY_A ) + : $wpdb->get_results( $wpdb->prepare( $full_sql, ...$params ), ARRAY_A ); + + return [ + 'rows' => $rows ?: [], + 'total' => $total, + ]; + } + + /** + * Get counts per analysis_type for the filter badges. + * + * Side effects: database SELECT. + * + * @return array Map of analysis_type => count. + */ + private function get_type_counts(): array { + global $wpdb; + $table = $wpdb->prefix . 'prautoblogger_analysis_results'; + + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $rows = $wpdb->get_results( + "SELECT analysis_type, COUNT(*) AS cnt FROM {$table} GROUP BY analysis_type ORDER BY cnt DESC", + ARRAY_A + ); + + $counts = []; + foreach ( $rows ?: [] as $row ) { + $counts[ $row['analysis_type'] ] = (int) $row['cnt']; + } + return $counts; + } +} diff --git a/includes/class-prautoblogger.php b/includes/class-prautoblogger.php index f9b884c..cb641df 100644 --- a/includes/class-prautoblogger.php +++ b/includes/class-prautoblogger.php @@ -73,6 +73,7 @@ class PRAutoBlogger { $review_queue = new PRAutoBlogger_Review_Queue(); add_action( 'admin_menu', [ $review_queue, 'on_register_menu' ] ); + add_action( 'admin_menu', [ new PRAutoBlogger_Ideas_Browser(), 'on_register_menu' ] ); add_action( 'admin_menu', [ new PRAutoBlogger_Log_Viewer(), 'on_register_menu' ] ); ( new PRAutoBlogger_Post_List_Columns() )->register(); diff --git a/prautoblogger.php b/prautoblogger.php index a1795fc..5c01fc7 100644 --- a/prautoblogger.php +++ b/prautoblogger.php @@ -8,7 +8,7 @@ declare(strict_types=1); * Plugin Name: PRAutoBlogger * Plugin URI: https://peptiderepo.com/prautoblogger * Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics. - * Version: 0.5.0 + * Version: 0.5.1 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: PeptideRepo @@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) { | and limits without magic strings. */ -define( 'PRAUTOBLOGGER_VERSION', '0.5.0' ); +define( 'PRAUTOBLOGGER_VERSION', '0.5.1' ); define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' ); define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ ); define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); diff --git a/templates/admin/ideas-browser.php b/templates/admin/ideas-browser.php new file mode 100644 index 0000000..498185d --- /dev/null +++ b/templates/admin/ideas-browser.php @@ -0,0 +1,153 @@ + count for filter badges. + * $paged (int) — current page number. + * + * @see admin/class-ideas-browser.php — Renders this template. + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +$base_url = admin_url( 'admin.php?page=prautoblogger-ideas' ); +?> +
+

+

+ +

+ + +
+ + + + $count ) : ?> + + + + +
+ + + +
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+ +
+ +
+ + + + + = 70 ? '#00a32a' : ( $score_pct >= 40 ? '#dba617' : '#d63638' ); + ?> +
+
+
+
+ % +
+
+ +
    + +
  • + + 4 ) : ?> +
  • + more
  • + +
+ + +
+ + + + + +
+ +
+ +
+ + 1 ) : ?> +
+ add_query_arg( 'paged', '%#%' ), + 'format' => '', + 'current' => $paged, + 'total' => $total_pages, + 'prev_text' => '«', + 'next_text' => '»', + ] ) ); + ?> +
+ + +