You've already forked wc-licensed-product
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 <noreply@anthropic.com>
This commit is contained in:
14
CHANGELOG.md
14
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,506 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* WooCommerce Analytics Integration Controller
|
||||
*
|
||||
* @package Jeremias\WcLicensedProduct\Admin
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jeremias\WcLicensedProduct\Admin;
|
||||
|
||||
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
||||
|
||||
/**
|
||||
* Integrates license statistics with WooCommerce Analytics
|
||||
*/
|
||||
class AnalyticsController
|
||||
{
|
||||
private LicenseManager $licenseManager;
|
||||
|
||||
public function __construct(LicenseManager $licenseManager)
|
||||
{
|
||||
$this->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 '<div class="wrap"><h1>License Statistics - DEBUG TEST</h1><p>If you see this, the callback is working.</p></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback rendering for statistics page
|
||||
*/
|
||||
private function renderStatisticsPageFallback(array $stats): void
|
||||
{
|
||||
?>
|
||||
<div class="wrap wclp-statistics">
|
||||
<h1><?php esc_html_e('License Statistics', 'wc-licensed-product'); ?></h1>
|
||||
|
||||
<div class="wclp-stats-overview">
|
||||
<div class="wclp-stat-cards">
|
||||
<div class="wclp-stat-card">
|
||||
<h3><?php esc_html_e('Total Licenses', 'wc-licensed-product'); ?></h3>
|
||||
<span class="wclp-stat-number"><?php echo esc_html($stats['total']); ?></span>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-active">
|
||||
<h3><?php esc_html_e('Active', 'wc-licensed-product'); ?></h3>
|
||||
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status']['active'] ?? 0); ?></span>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-inactive">
|
||||
<h3><?php esc_html_e('Inactive', 'wc-licensed-product'); ?></h3>
|
||||
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status']['inactive'] ?? 0); ?></span>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-expired">
|
||||
<h3><?php esc_html_e('Expired', 'wc-licensed-product'); ?></h3>
|
||||
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status']['expired'] ?? 0); ?></span>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-revoked">
|
||||
<h3><?php esc_html_e('Revoked', 'wc-licensed-product'); ?></h3>
|
||||
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status']['revoked'] ?? 0); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($stats['expiring_soon'] > 0): ?>
|
||||
<div class="notice notice-warning">
|
||||
<p>
|
||||
<strong><?php esc_html_e('Attention:', 'wc-licensed-product'); ?></strong>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %d: number of licenses */
|
||||
_n(
|
||||
'%d license is expiring within the next 30 days.',
|
||||
'%d licenses are expiring within the next 30 days.',
|
||||
$stats['expiring_soon'],
|
||||
'wc-licensed-product'
|
||||
),
|
||||
$stats['expiring_soon']
|
||||
);
|
||||
?>
|
||||
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>">
|
||||
<?php esc_html_e('View Licenses', 'wc-licensed-product'); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wclp-stats-details">
|
||||
<div class="wclp-stat-box">
|
||||
<h3><?php esc_html_e('License Types', 'wc-licensed-product'); ?></h3>
|
||||
<table class="widefat striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><?php esc_html_e('Lifetime Licenses', 'wc-licensed-product'); ?></td>
|
||||
<td class="wclp-stat-value"><?php echo esc_html($stats['lifetime'] ?? 0); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php esc_html_e('Time-limited Licenses', 'wc-licensed-product'); ?></td>
|
||||
<td class="wclp-stat-value"><?php echo esc_html($stats['expiring'] ?? 0); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><?php esc_html_e('Expiring Soon (30 days)', 'wc-licensed-product'); ?></td>
|
||||
<td class="wclp-stat-value"><?php echo esc_html($stats['expiring_soon'] ?? 0); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="wclp-stat-box">
|
||||
<h3><?php esc_html_e('Top Products by Licenses', 'wc-licensed-product'); ?></h3>
|
||||
<?php if (empty($stats['by_product'])): ?>
|
||||
<p class="description"><?php esc_html_e('No license data available yet.', 'wc-licensed-product'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e('Product', 'wc-licensed-product'); ?></th>
|
||||
<th class="wclp-stat-value"><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stats['by_product'] as $product): ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($product['product_name']); ?></td>
|
||||
<td class="wclp-stat-value"><?php echo esc_html($product['count']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wclp-stat-box">
|
||||
<h3><?php esc_html_e('Top Domains', 'wc-licensed-product'); ?></h3>
|
||||
<?php if (empty($stats['top_domains'])): ?>
|
||||
<p class="description"><?php esc_html_e('No license data available yet.', 'wc-licensed-product'); ?></p>
|
||||
<?php else: ?>
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e('Domain', 'wc-licensed-product'); ?></th>
|
||||
<th class="wclp-stat-value"><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stats['top_domains'] as $domain): ?>
|
||||
<tr>
|
||||
<td><code><?php echo esc_html($domain['domain']); ?></code></td>
|
||||
<td class="wclp-stat-value"><?php echo esc_html($domain['count']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-chart-section">
|
||||
<h3><?php esc_html_e('Licenses Created (Last 12 Months)', 'wc-licensed-product'); ?></h3>
|
||||
<?php if (empty($stats['monthly'])): ?>
|
||||
<p class="description"><?php esc_html_e('No license data available yet.', 'wc-licensed-product'); ?></p>
|
||||
<?php else: ?>
|
||||
<div class="wclp-chart-container">
|
||||
<div class="wclp-bar-chart">
|
||||
<?php
|
||||
$maxValue = max(1, max($stats['monthly']));
|
||||
foreach ($stats['monthly'] as $month => $count):
|
||||
$height = ($count / $maxValue * 100);
|
||||
?>
|
||||
<div class="wclp-bar-wrapper">
|
||||
<div class="wclp-bar" style="height: <?php echo esc_attr($height); ?>%;" title="<?php echo esc_attr($count); ?> <?php esc_attr_e('licenses', 'wc-licensed-product'); ?>">
|
||||
<span class="wclp-bar-value"><?php echo esc_html($count); ?></span>
|
||||
</div>
|
||||
<span class="wclp-bar-label"><?php echo esc_html(date_i18n('M Y', strtotime($month . '-01'))); ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-stats-actions">
|
||||
<h2><?php esc_html_e('Quick Actions', 'wc-licensed-product'); ?></h2>
|
||||
<div class="wclp-action-buttons">
|
||||
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>" class="button button-primary">
|
||||
<span class="dashicons dashicons-admin-network"></span>
|
||||
<?php esc_html_e('Manage Licenses', 'wc-licensed-product'); ?>
|
||||
</a>
|
||||
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses&action=export_csv')); ?>" class="button">
|
||||
<span class="dashicons dashicons-download"></span>
|
||||
<?php esc_html_e('Export to CSV', 'wc-licensed-product'); ?>
|
||||
</a>
|
||||
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-settings&tab=licensed_product')); ?>" class="button">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<?php esc_html_e('Settings', 'wc-licensed-product'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-api-info">
|
||||
<h3><?php esc_html_e('REST API Endpoints', 'wc-licensed-product'); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e('The following REST API endpoints are available for retrieving license statistics:', 'wc-licensed-product'); ?>
|
||||
</p>
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e('Endpoint', 'wc-licensed-product'); ?></th>
|
||||
<th><?php esc_html_e('Description', 'wc-licensed-product'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>GET /wc-licensed-product/v1/analytics/stats</code></td>
|
||||
<td><?php esc_html_e('Get license statistics with time-series data', 'wc-licensed-product'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>GET /wc-licensed-product/v1/analytics/products</code></td>
|
||||
<td><?php esc_html_e('Get license counts by product', 'wc-licensed-product'); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ declare(strict_types=1);
|
||||
namespace Jeremias\WcLicensedProduct;
|
||||
|
||||
use Jeremias\WcLicensedProduct\Admin\AdminController;
|
||||
use Jeremias\WcLicensedProduct\Admin\AnalyticsController;
|
||||
use Jeremias\WcLicensedProduct\Admin\OrderLicenseController;
|
||||
use Jeremias\WcLicensedProduct\Admin\SettingsController;
|
||||
use Jeremias\WcLicensedProduct\Admin\VersionAdminController;
|
||||
@@ -57,11 +56,6 @@ final class Plugin
|
||||
*/
|
||||
private DownloadController $downloadController;
|
||||
|
||||
/**
|
||||
* Analytics controller
|
||||
*/
|
||||
private ?AnalyticsController $analyticsController = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*/
|
||||
@@ -139,8 +133,6 @@ final class Plugin
|
||||
new VersionAdminController($this->versionManager);
|
||||
new OrderLicenseController($this->licenseManager);
|
||||
new SettingsController();
|
||||
$this->analyticsController = new AnalyticsController($this->licenseManager);
|
||||
$this->analyticsController->init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
<div class="wrap wclp-statistics">
|
||||
<h1>{{ __('License Statistics') }}</h1>
|
||||
|
||||
<div class="wclp-stats-overview">
|
||||
<div class="wclp-stat-cards">
|
||||
<div class="wclp-stat-card wclp-stat-total">
|
||||
<div class="wclp-stat-icon"><span class="dashicons dashicons-admin-network"></span></div>
|
||||
<div class="wclp-stat-content">
|
||||
<span class="wclp-stat-number">{{ stats.total }}</span>
|
||||
<span class="wclp-stat-label">{{ __('Total Licenses') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-active">
|
||||
<div class="wclp-stat-icon"><span class="dashicons dashicons-yes-alt"></span></div>
|
||||
<div class="wclp-stat-content">
|
||||
<span class="wclp-stat-number">{{ stats.by_status.active }}</span>
|
||||
<span class="wclp-stat-label">{{ __('Active') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-inactive">
|
||||
<div class="wclp-stat-icon"><span class="dashicons dashicons-marker"></span></div>
|
||||
<div class="wclp-stat-content">
|
||||
<span class="wclp-stat-number">{{ stats.by_status.inactive }}</span>
|
||||
<span class="wclp-stat-label">{{ __('Inactive') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-expired">
|
||||
<div class="wclp-stat-icon"><span class="dashicons dashicons-calendar-alt"></span></div>
|
||||
<div class="wclp-stat-content">
|
||||
<span class="wclp-stat-number">{{ stats.by_status.expired }}</span>
|
||||
<span class="wclp-stat-label">{{ __('Expired') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wclp-stat-card wclp-stat-revoked">
|
||||
<div class="wclp-stat-icon"><span class="dashicons dashicons-dismiss"></span></div>
|
||||
<div class="wclp-stat-content">
|
||||
<span class="wclp-stat-number">{{ stats.by_status.revoked }}</span>
|
||||
<span class="wclp-stat-label">{{ __('Revoked') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if stats.expiring_soon > 0 %}
|
||||
<div class="notice notice-warning">
|
||||
<p>
|
||||
<span class="dashicons dashicons-warning"></span>
|
||||
<strong>{{ __('Attention:') }}</strong>
|
||||
{{ stats.expiring_soon }} {{ stats.expiring_soon == 1 ? __('license is') : __('licenses are') }}
|
||||
{{ __('expiring within the next 30 days.') }}
|
||||
<a href="{{ admin_url }}?page=wc-licenses">{{ __('View Licenses') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="wclp-stats-details">
|
||||
<div class="wclp-stat-box">
|
||||
<h3>{{ __('License Types') }}</h3>
|
||||
<table class="widefat striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ __('Lifetime Licenses') }}</td>
|
||||
<td class="wclp-stat-value">{{ stats.lifetime }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ __('Time-limited Licenses') }}</td>
|
||||
<td class="wclp-stat-value">{{ stats.expiring }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ __('Expiring Soon (30 days)') }}</td>
|
||||
<td class="wclp-stat-value {% if stats.expiring_soon > 0 %}wclp-warning{% endif %}">
|
||||
{{ stats.expiring_soon }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="wclp-stat-box">
|
||||
<h3>{{ __('Top Products by Licenses') }}</h3>
|
||||
{% if stats.by_product is empty %}
|
||||
<p class="description">{{ __('No license data available yet.') }}</p>
|
||||
{% else %}
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Product') }}</th>
|
||||
<th class="wclp-stat-value">{{ __('Licenses') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for product in stats.by_product %}
|
||||
<tr>
|
||||
<td>{{ esc_html(product.product_name) }}</td>
|
||||
<td class="wclp-stat-value">{{ product.count }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="wclp-stat-box">
|
||||
<h3>{{ __('Top Domains') }}</h3>
|
||||
{% if stats.top_domains is empty %}
|
||||
<p class="description">{{ __('No license data available yet.') }}</p>
|
||||
{% else %}
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Domain') }}</th>
|
||||
<th class="wclp-stat-value">{{ __('Licenses') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for domain in stats.top_domains %}
|
||||
<tr>
|
||||
<td><code>{{ esc_html(domain.domain) }}</code></td>
|
||||
<td class="wclp-stat-value">{{ domain.count }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-chart-section">
|
||||
<h3>{{ __('Licenses Created (Last 12 Months)') }}</h3>
|
||||
{% if stats.monthly is empty %}
|
||||
<p class="description">{{ __('No license data available yet.') }}</p>
|
||||
{% else %}
|
||||
<div class="wclp-chart-container">
|
||||
<div class="wclp-bar-chart" id="wclp-monthly-chart">
|
||||
{% 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 %}
|
||||
<div class="wclp-bar-wrapper">
|
||||
<div class="wclp-bar" style="height: {{ (count / max_value * 100)|round }}%;" title="{{ count }} {{ __('licenses') }}">
|
||||
<span class="wclp-bar-value">{{ count }}</span>
|
||||
</div>
|
||||
<span class="wclp-bar-label">{{ month|date('M Y') }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-stats-actions">
|
||||
<h2>{{ __('Quick Actions') }}</h2>
|
||||
<div class="wclp-action-buttons">
|
||||
<a href="{{ admin_url }}?page=wc-licenses" class="button button-primary">
|
||||
<span class="dashicons dashicons-admin-network"></span>
|
||||
{{ __('Manage Licenses') }}
|
||||
</a>
|
||||
<a href="{{ admin_url }}?page=wc-licenses&action=export_csv" class="button">
|
||||
<span class="dashicons dashicons-download"></span>
|
||||
{{ __('Export to CSV') }}
|
||||
</a>
|
||||
<a href="{{ admin_url }}?page=wc-settings&tab=licensed_product" class="button">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
{{ __('Settings') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wclp-api-info">
|
||||
<h3>{{ __('REST API Endpoints') }}</h3>
|
||||
<p class="description">
|
||||
{{ __('The following REST API endpoints are available for retrieving license statistics:') }}
|
||||
</p>
|
||||
<table class="widefat striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Endpoint') }}</th>
|
||||
<th>{{ __('Description') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>GET {{ rest_url }}stats</code></td>
|
||||
<td>{{ __('Get license statistics with time-series data') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>GET {{ rest_url }}products</code></td>
|
||||
<td>{{ __('Get license counts by product') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user