Skip to content

Phase 14: Admin UI

Build the full admin interface: dashboard with KPI widgets, license list/edit screens using WP_List_Table, and a Settings API–backed configuration page.

  • src/Admin/Menu.php
  • src/Admin/Screens/DashboardScreen.php
  • src/Admin/Screens/LicenseListTable.php
  • src/Admin/Settings/SettingsPage.php

Menu registers all admin pages in a admin_menu hook and sets up the Settings API in admin_init:

License Manager (top-level)
├── Dashboard
├── Licenses
├── Generators
├── Subscriptions
├── API Keys
└── Settings

All save handlers call check_admin_referer() and current_user_can('manage_options') before processing any $_POST data.

Four KPI widgets rendered in a 2×2 CSS grid:

WidgetData
Active LicensesCount of status = 1
Expiring SoonCount expiring within 30 days
Active SeatsCount of active machines
Revenue (MTD)Sum of completed renewals this month

Below the KPIs: “Recent Activations” table (last 10 activation log entries) and “Active Anomalies” banner (fires AnalyticsService::detect_anomalies() and highlights any hits).

Extends WP_List_Table. Columns:

ColumnNotes
cbCheckbox for bulk actions
license_keyTruncated: ABCD-****-****-WXYZ + copy button
status<span class="wplm-badge wplm-status-{n}">
product_idWooCommerce product name if available
activation_countn / max
expires_atHuman date or “Never”
created_atHuman date

Bulk actions: bulk-revoke, bulk-suspend, bulk-export (CSV).

render_page() dispatches to the list view or the edit form based on the presence of $_GET['id'].

Edit form: Shows all license fields (status, max_activations, expires_at, notes), submits via POST to admin-post.php?action=wplm_save_license with nonce wplm_save_license.

Uses the WordPress Settings API. Six sections:

SectionFields
GeneralLicense server name, site URL override
SecurityEd25519 public key display, keypair re-roll button, AES key status
ActivationDefault max activations, overage strategy, heartbeat interval, zombie window
MonitoringAnomaly detection on/off, thresholds
SubscriptionsDefault billing periods, trial days, grace period
DunningRetry schedule (days), max retries, notification emails

validate_settings() sanitizes all inputs: absint() for integers, sanitize_text_field() for strings, esc_url_raw() for URLs.