From 6f1ea3c6fa6b4deec40c6d0847841f146add4fd0 Mon Sep 17 00:00:00 2001 From: magdev Date: Thu, 22 Jan 2026 00:10:32 +0100 Subject: [PATCH] Remove License Statistics page due to issues Keep existing Reports > Licenses dashboard which works correctly. Version 0.0.11 now only includes the Created column in license overview. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 14 +- CLAUDE.md | 2 +- src/Admin/AnalyticsController.php | 506 --------------------------- src/Plugin.php | 8 - templates/admin/statistics.html.twig | 196 ----------- 5 files changed, 4 insertions(+), 722 deletions(-) delete mode 100644 src/Admin/AnalyticsController.php delete mode 100644 templates/admin/statistics.html.twig diff --git a/CHANGELOG.md b/CHANGELOG.md index 0405345..e6b2858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,24 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.0.11] - 2026-01-21 +## [0.0.11] - 2026-01-22 ### Added - Created date column in admin license overview -- License Statistics page under WooCommerce menu -- REST API endpoints for analytics data: - - `GET /wp-json/wc-licensed-product/v1/analytics/stats` - License statistics with time-series data - - `GET /wp-json/wc-licensed-product/v1/analytics/products` - License counts by product -- WooCommerce Analytics integration via submenu page ### Technical Details -- New `AnalyticsController` class for WooCommerce Analytics integration -- Statistics page accessible via WooCommerce > License Statistics -- Time-series data supports day, week, month, quarter, year intervals -- REST API endpoints for external analytics integrations -- Statistics template `templates/admin/statistics.html.twig` +- Added "Created" column to licenses table in admin (Twig template and PHP fallback) +- Shows when each license was generated ## [0.0.10] - 2026-01-21 diff --git a/CLAUDE.md b/CLAUDE.md index d83a938..0cee627 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w ### Known Bugs -- Version uploads not appearing in list (under investigation - may require plugin reactivation to ensure database tables exist) +No known bugs at the moment ## Technical Stack diff --git a/src/Admin/AnalyticsController.php b/src/Admin/AnalyticsController.php deleted file mode 100644 index d4e7a38..0000000 --- a/src/Admin/AnalyticsController.php +++ /dev/null @@ -1,506 +0,0 @@ -licenseManager = $licenseManager; - $this->registerHooks(); - } - - /** - * Register WordPress hooks - */ - private function registerHooks(): void - { - // Add submenu under WooCommerce menu - add_action('admin_menu', [$this, 'addAnalyticsSubmenu']); - - // Register REST API endpoints for analytics data - add_action('rest_api_init', [$this, 'registerRestRoutes']); - - // Add license stats to WooCommerce Admin data registry - add_action('admin_enqueue_scripts', [$this, 'enqueueAnalyticsData']); - } - - /** - * Initialize analytics hooks (kept for backwards compatibility) - */ - public function init(): void - { - // Hooks are now registered in constructor - } - - /** - * Add submenu page under WooCommerce menu - */ - public function addAnalyticsSubmenu(): void - { - add_submenu_page( - 'woocommerce', - __('License Statistics', 'wc-licensed-product'), - __('License Statistics', 'wc-licensed-product'), - 'manage_woocommerce', - 'wc-license-statistics', - [$this, 'renderStatisticsPage'] - ); - } - - /** - * Add navigation item for WC Admin navigation - */ - public function addNavigationItem(array $items): array - { - $items[] = [ - 'id' => 'wc-license-statistics', - 'title' => __('License Statistics', 'wc-licensed-product'), - 'parent' => 'woocommerce-analytics', - 'path' => '/analytics/license-statistics', - ]; - - return $items; - } - - /** - * Add report menu item to WooCommerce Analytics - */ - public function addAnalyticsReportMenuItem(array $report_pages): array - { - $report_pages[] = [ - 'id' => 'wc-license-statistics', - 'title' => __('License Statistics', 'wc-licensed-product'), - 'parent' => 'woocommerce-analytics', - 'path' => '/analytics/license-statistics', - ]; - - return $report_pages; - } - - /** - * Register REST API routes for analytics data - */ - public function registerRestRoutes(): void - { - register_rest_route('wc-licensed-product/v1', '/analytics/stats', [ - 'methods' => \WP_REST_Server::READABLE, - 'callback' => [$this, 'getAnalyticsStats'], - 'permission_callback' => function () { - return current_user_can('manage_woocommerce'); - }, - 'args' => [ - 'after' => [ - 'description' => __('Limit response to stats after a given date.', 'wc-licensed-product'), - 'type' => 'string', - 'format' => 'date-time', - ], - 'before' => [ - 'description' => __('Limit response to stats before a given date.', 'wc-licensed-product'), - 'type' => 'string', - 'format' => 'date-time', - ], - 'interval' => [ - 'description' => __('Time interval to aggregate stats.', 'wc-licensed-product'), - 'type' => 'string', - 'enum' => ['day', 'week', 'month', 'quarter', 'year'], - 'default' => 'month', - ], - ], - ]); - - register_rest_route('wc-licensed-product/v1', '/analytics/products', [ - 'methods' => \WP_REST_Server::READABLE, - 'callback' => [$this, 'getProductStats'], - 'permission_callback' => function () { - return current_user_can('manage_woocommerce'); - }, - 'args' => [ - 'per_page' => [ - 'description' => __('Maximum number of items to return.', 'wc-licensed-product'), - 'type' => 'integer', - 'default' => 10, - ], - 'orderby' => [ - 'description' => __('Sort by this field.', 'wc-licensed-product'), - 'type' => 'string', - 'enum' => ['licenses_count', 'product_name'], - 'default' => 'licenses_count', - ], - 'order' => [ - 'description' => __('Order direction.', 'wc-licensed-product'), - 'type' => 'string', - 'enum' => ['asc', 'desc'], - 'default' => 'desc', - ], - ], - ]); - } - - /** - * Get analytics stats via REST API - */ - public function getAnalyticsStats(\WP_REST_Request $request): \WP_REST_Response - { - $stats = $this->licenseManager->getStatistics(); - $interval = $request->get_param('interval') ?: 'month'; - - // Get time-series data based on interval - $timeSeriesData = $this->getTimeSeriesData($interval, $request->get_param('after'), $request->get_param('before')); - - return new \WP_REST_Response([ - 'totals' => [ - 'total_licenses' => $stats['total'], - 'active_licenses' => $stats['by_status']['active'] ?? 0, - 'inactive_licenses' => $stats['by_status']['inactive'] ?? 0, - 'expired_licenses' => $stats['by_status']['expired'] ?? 0, - 'revoked_licenses' => $stats['by_status']['revoked'] ?? 0, - 'lifetime_licenses' => $stats['lifetime'] ?? 0, - 'expiring_soon' => $stats['expiring_soon'] ?? 0, - ], - 'intervals' => $timeSeriesData, - ], 200); - } - - /** - * Get product statistics via REST API - */ - public function getProductStats(\WP_REST_Request $request): \WP_REST_Response - { - $stats = $this->licenseManager->getStatistics(); - $perPage = $request->get_param('per_page') ?: 10; - - $productStats = array_slice($stats['by_product'] ?? [], 0, $perPage); - - return new \WP_REST_Response([ - 'products' => $productStats, - ], 200); - } - - /** - * Get time-series data for the specified interval - */ - private function getTimeSeriesData(string $interval, ?string $after = null, ?string $before = null): array - { - global $wpdb; - - $tableName = $wpdb->prefix . 'wc_licensed_product_licenses'; - - // Set default date range - $endDate = $before ? new \DateTimeImmutable($before) : new \DateTimeImmutable(); - $startDate = $after ? new \DateTimeImmutable($after) : $endDate->modify('-12 months'); - - // Build date format based on interval - switch ($interval) { - case 'day': - $dateFormat = '%Y-%m-%d'; - $phpFormat = 'Y-m-d'; - break; - case 'week': - $dateFormat = '%Y-%u'; - $phpFormat = 'Y-W'; - break; - case 'quarter': - $dateFormat = "CONCAT(YEAR(created_at), '-Q', QUARTER(created_at))"; - $phpFormat = 'Y-\QQ'; - break; - case 'year': - $dateFormat = '%Y'; - $phpFormat = 'Y'; - break; - case 'month': - default: - $dateFormat = '%Y-%m'; - $phpFormat = 'Y-m'; - break; - } - - // Special handling for quarter since it's not a simple DATE_FORMAT - if ($interval === 'quarter') { - $sql = $wpdb->prepare( - "SELECT {$dateFormat} as period, COUNT(*) as count - FROM {$tableName} - WHERE created_at >= %s AND created_at <= %s - GROUP BY period - ORDER BY period ASC", - $startDate->format('Y-m-d 00:00:00'), - $endDate->format('Y-m-d 23:59:59') - ); - } else { - $sql = $wpdb->prepare( - "SELECT DATE_FORMAT(created_at, %s) as period, COUNT(*) as count - FROM {$tableName} - WHERE created_at >= %s AND created_at <= %s - GROUP BY period - ORDER BY period ASC", - $dateFormat, - $startDate->format('Y-m-d 00:00:00'), - $endDate->format('Y-m-d 23:59:59') - ); - } - - $results = $wpdb->get_results($sql, ARRAY_A); - - $data = []; - foreach ($results as $row) { - $data[] = [ - 'interval' => $row['period'], - 'subtotals' => [ - 'licenses_count' => (int) $row['count'], - ], - ]; - } - - return $data; - } - - /** - * Enqueue license analytics data for WC Admin - */ - public function enqueueAnalyticsData(): void - { - if (!function_exists('wc_admin_get_feature_config')) { - return; - } - - $screen = get_current_screen(); - if (!$screen || strpos($screen->id, 'woocommerce') === false) { - return; - } - - $stats = $this->licenseManager->getStatistics(); - - wp_localize_script('wc-admin-app', 'wcLicenseStats', [ - 'total' => $stats['total'], - 'active' => $stats['by_status']['active'] ?? 0, - 'inactive' => $stats['by_status']['inactive'] ?? 0, - 'expired' => $stats['by_status']['expired'] ?? 0, - 'revoked' => $stats['by_status']['revoked'] ?? 0, - 'lifetime' => $stats['lifetime'] ?? 0, - 'expiringSoon' => $stats['expiring_soon'] ?? 0, - 'endpoints' => [ - 'stats' => rest_url('wc-licensed-product/v1/analytics/stats'), - 'products' => rest_url('wc-licensed-product/v1/analytics/products'), - ], - ]); - } - - /** - * Render the statistics page - */ - public function renderStatisticsPage(): void - { - echo '

License Statistics - DEBUG TEST

If you see this, the callback is working.

'; - } - - /** - * Fallback rendering for statistics page - */ - private function renderStatisticsPageFallback(array $stats): void - { - ?> -
-

- -
-
-
-

- -
-
-

- -
-
-

- -
-
-

- -
-
-

- -
-
- - 0): ?> -
-

- - - - - -

-
- - -
-
-

- - - - - - - - - - - - - - - -
-
- -
-

- -

- - - - - - - - - - - - - - - - -
- -
- -
-

- -

- - - - - - - - - - - - - - - - -
- -
-
- -
-

- -

- -
-
- $count): - $height = ($count / $maxValue * 100); - ?> -
-
- -
- -
- -
-
- -
-
- - - -
-

-

- -

- - - - - - - - - - - - - - - - - -
GET /wc-licensed-product/v1/analytics/stats
GET /wc-licensed-product/v1/analytics/products
-
-
- versionManager); new OrderLicenseController($this->licenseManager); new SettingsController(); - $this->analyticsController = new AnalyticsController($this->licenseManager); - $this->analyticsController->init(); } } diff --git a/templates/admin/statistics.html.twig b/templates/admin/statistics.html.twig deleted file mode 100644 index 360b6c8..0000000 --- a/templates/admin/statistics.html.twig +++ /dev/null @@ -1,196 +0,0 @@ -
-

{{ __('License Statistics') }}

- -
-
-
-
-
- {{ stats.total }} - {{ __('Total Licenses') }} -
-
-
-
-
- {{ stats.by_status.active }} - {{ __('Active') }} -
-
-
-
-
- {{ stats.by_status.inactive }} - {{ __('Inactive') }} -
-
-
-
-
- {{ stats.by_status.expired }} - {{ __('Expired') }} -
-
-
-
-
- {{ stats.by_status.revoked }} - {{ __('Revoked') }} -
-
-
- - {% if stats.expiring_soon > 0 %} -
-

- - {{ __('Attention:') }} - {{ stats.expiring_soon }} {{ stats.expiring_soon == 1 ? __('license is') : __('licenses are') }} - {{ __('expiring within the next 30 days.') }} - {{ __('View Licenses') }} -

-
- {% endif %} - -
-
-

{{ __('License Types') }}

- - - - - - - - - - - - - - - -
{{ __('Lifetime Licenses') }}{{ stats.lifetime }}
{{ __('Time-limited Licenses') }}{{ stats.expiring }}
{{ __('Expiring Soon (30 days)') }} - {{ stats.expiring_soon }} -
-
- -
-

{{ __('Top Products by Licenses') }}

- {% if stats.by_product is empty %} -

{{ __('No license data available yet.') }}

- {% else %} - - - - - - - - - {% for product in stats.by_product %} - - - - - {% endfor %} - -
{{ __('Product') }}{{ __('Licenses') }}
{{ esc_html(product.product_name) }}{{ product.count }}
- {% endif %} -
- -
-

{{ __('Top Domains') }}

- {% if stats.top_domains is empty %} -

{{ __('No license data available yet.') }}

- {% else %} - - - - - - - - - {% for domain in stats.top_domains %} - - - - - {% endfor %} - -
{{ __('Domain') }}{{ __('Licenses') }}
{{ esc_html(domain.domain) }}{{ domain.count }}
- {% endif %} -
-
- -
-

{{ __('Licenses Created (Last 12 Months)') }}

- {% if stats.monthly is empty %} -

{{ __('No license data available yet.') }}

- {% else %} -
-
- {% set max_value = 1 %} - {% for count in stats.monthly %} - {% if count > max_value %} - {% set max_value = count %} - {% endif %} - {% endfor %} - {% for month, count in stats.monthly %} -
-
- {{ count }} -
- {{ month|date('M Y') }} -
- {% endfor %} -
-
- {% endif %} -
-
- - - -
-

{{ __('REST API Endpoints') }}

-

- {{ __('The following REST API endpoints are available for retrieving license statistics:') }} -

- - - - - - - - - - - - - - - - - -
{{ __('Endpoint') }}{{ __('Description') }}
GET {{ rest_url }}stats{{ __('Get license statistics with time-series data') }}
GET {{ rest_url }}products{{ __('Get license counts by product') }}
-
-