PR Vision v0.2.1: dashboard config-marker + KPI health pill + auto-deploy pipeline (#4)
Wire the deferred dashboard visuals (trendline config-change marker from config_version data; KPI per-model health pill) + add .github/workflows/deploy.yml (CI-gated rsync to the prod plugin dir + LiteSpeed purge + health check on push to main) replacing manual rsync. QA: APPROVE — qa-reviews/pr-vision/2026-06-14-f5cfa39f60db22ac9f223ca1d95ea4954b43d71f.md Agent-Session: pr-vision v0.2.1 merge
This commit is contained in:
@@ -10,9 +10,10 @@ declare(strict_types=1);
|
||||
/**
|
||||
* PR Vision top-level admin page.
|
||||
*
|
||||
* Registers the "PR Vision" menu item (manage_options), enqueues Chart.js,
|
||||
* handles the "Run now" POST action (nonce + capability), and delegates
|
||||
* rendering to the registered panels via PRV_Collector_Registry.
|
||||
* Registers the "PR Vision" menu item (manage_options), enqueues Chart.js
|
||||
* and the dashboard stylesheet, handles the "Run now" POST action (nonce +
|
||||
* capability), and delegates rendering to the registered panels via
|
||||
* PRV_Collector_Registry.
|
||||
*
|
||||
* Who triggers: PRV_Plugin::init() → is_admin() guard.
|
||||
* Dependencies: PRV_Collector_Registry, PRV_Probe_Runner.
|
||||
@@ -20,6 +21,7 @@ declare(strict_types=1);
|
||||
* @see class-prv-plugin.php — Calls register_hooks().
|
||||
* @see class-prv-collector-registry.php — Provides panels.
|
||||
* @see class-prv-probe-runner.php — Invoked by the Run now action.
|
||||
* @see class-prv-ai-visibility-panel.php — Panel renderer, consumes CSS added here.
|
||||
* @see ARCHITECTURE.md — §Admin dashboard.
|
||||
* @package PrVision
|
||||
*/
|
||||
@@ -73,7 +75,10 @@ class PRV_Admin_Page {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Chart.js on the PR Vision page only.
|
||||
* Enqueue Chart.js and dashboard styles on the PR Vision page only.
|
||||
*
|
||||
* Chart.js is loaded from the jsdelivr CDN. The dashboard panel degrades
|
||||
* gracefully when the CDN is blocked (typeof guard + .prv-noscript fallback).
|
||||
*
|
||||
* @param string $hook Current admin page hook.
|
||||
*
|
||||
@@ -92,16 +97,7 @@ class PRV_Admin_Page {
|
||||
true
|
||||
);
|
||||
|
||||
wp_add_inline_style(
|
||||
'wp-admin',
|
||||
'.prv-meta-bar{display:flex;gap:20px;margin:10px 0 15px;flex-wrap:wrap;}
|
||||
.prv-meta-item{background:#f6f7f7;padding:6px 12px;border-radius:4px;font-size:13px;}
|
||||
.prv-card{background:#fff;border:1px solid #c3c4c7;padding:16px 20px;margin-bottom:20px;border-radius:4px;}
|
||||
.prv-card h2{margin-top:0;}
|
||||
.prv-proxy-note{margin:10px 0;}
|
||||
.prv-standings th,.prv-standings td{vertical-align:middle;}
|
||||
canvas#prv-trendline-chart{max-height:300px;}'
|
||||
);
|
||||
wp_add_inline_style( 'wp-admin', $this->get_dashboard_css() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +154,6 @@ class PRV_Admin_Page {
|
||||
|
||||
$this->render_run_done_notice();
|
||||
|
||||
// "Run now" button form.
|
||||
echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
|
||||
echo '<input type="hidden" name="action" value="prv_run_now">';
|
||||
wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD );
|
||||
@@ -204,4 +199,77 @@ class PRV_Admin_Page {
|
||||
);
|
||||
echo '</p></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dashboard CSS for the PR Vision admin page.
|
||||
*
|
||||
* Uses CSS custom properties matching the "Assay" dark design palette from
|
||||
* design/pr-vision/design-proposal.md. Scoped to .prv-* selectors so it
|
||||
* cannot leak into WP admin chrome.
|
||||
*
|
||||
* @return string CSS string.
|
||||
*/
|
||||
private function get_dashboard_css(): string {
|
||||
return '
|
||||
/* === PR Vision dashboard — "Assay" palette (dark-adapted) === */
|
||||
.prv-bento{display:grid;grid-template-columns:1.25fr 1fr 1fr;gap:16px;margin:18px 0 22px;}
|
||||
@media(max-width:820px){.prv-bento{grid-template-columns:1fr;}}
|
||||
.prv-tile{background:#1C2228;border:1px solid #3A4651;border-radius:12px;padding:20px 22px;position:relative;min-height:140px;display:flex;flex-direction:column;}
|
||||
.prv-tile-label{font-size:11.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#9AA7B2;margin-bottom:6px;}
|
||||
.prv-tile-big{font-size:42px;font-weight:700;color:#EEF2F5;line-height:1;font-variant-numeric:tabular-nums;margin-top:auto;}
|
||||
.prv-tile-big small{font-size:16px;font-weight:600;color:#9AA7B2;margin-left:3px;}
|
||||
.prv-tile-sub{font-size:12.5px;color:#9AA7B2;margin-top:8px;}
|
||||
.prv-tile-delta{font-size:12.5px;font-weight:600;margin-top:8px;}
|
||||
.prv-delta--up{color:#9BE635;} .prv-delta--down{color:#FF6B5E;} .prv-delta--flat{color:#9AA7B2;}
|
||||
/* health pill */
|
||||
.prv-health-pill{position:absolute;top:14px;right:14px;display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;padding:3px 9px 3px 7px;border-radius:999px;}
|
||||
.prv-health-dot{width:7px;height:7px;border-radius:50%;flex:0 0 7px;}
|
||||
.prv-health--ok{background:rgba(155,230,53,.15);color:#9BE635;} .prv-health--ok .prv-health-dot{background:#9BE635;}
|
||||
.prv-health--warn{background:rgba(255,157,77,.15);color:#FFB36E;} .prv-health--warn .prv-health-dot{background:#FF9D4D;}
|
||||
.prv-health--neutral{background:rgba(136,147,160,.14);color:#B4BDC7;} .prv-health--neutral .prv-health-dot{background:#8893A0;}
|
||||
/* cost tile */
|
||||
.prv-cost-row{display:flex;align-items:baseline;gap:4px;margin-top:auto;}
|
||||
.prv-cost-big{font-size:28px;font-weight:700;color:#EEF2F5;font-variant-numeric:tabular-nums;}
|
||||
.prv-cost-cap{font-size:13px;color:#9AA7B2;}
|
||||
.prv-meter{height:10px;border-radius:999px;background:#2A333C;margin-top:8px;border:1px solid #333D47;overflow:hidden;}
|
||||
.prv-meter>span{display:block;height:100%;border-radius:999px;background:#34C0CA;transition:width .3s ease;}
|
||||
.prv-meter--capped>span{background:#FF9D4D;}
|
||||
.prv-trunc-badge{display:flex;align-items:flex-start;gap:7px;margin-top:10px;background:rgba(255,157,77,.15);border:1px solid rgba(255,157,77,.42);border-radius:6px;padding:8px 10px;font-size:12px;color:#EEF2F5;line-height:1.4;}
|
||||
.prv-trunc-icon{color:#FFB36E;font-size:14px;flex:0 0 16px;margin-top:1px;}
|
||||
.prv-trunc-badge b{color:#FFB36E;}
|
||||
/* last run tile */
|
||||
.prv-tile-ts{font-size:20px;font-weight:700;color:#EEF2F5;margin-top:auto;line-height:1.2;}
|
||||
.prv-tile-meta{font-size:12.5px;color:#9AA7B2;margin-top:6px;}
|
||||
/* cards */
|
||||
.prv-card{background:#1C2228;border:1px solid #3A4651;border-radius:12px;margin:0 0 20px;}
|
||||
.prv-card-head{display:flex;align-items:center;justify-content:space-between;padding:18px 22px 0;}
|
||||
.prv-card-head h2{font-size:16px;font-weight:600;color:#EEF2F5;margin:0;}
|
||||
.prv-card-body{padding:10px 22px 20px;}
|
||||
/* chart */
|
||||
.prv-chartbox{position:relative;height:300px;max-height:300px;padding:14px 8px 0;overflow:hidden;}
|
||||
.prv-chartbox canvas{max-height:286px;}
|
||||
.prv-chart-fallback{display:none;height:100%;align-items:center;justify-content:center;text-align:center;color:#9AA7B2;font-size:13px;padding:0 24px;}
|
||||
.prv-chartbox.prv-noscript .prv-chart-fallback{display:flex;}
|
||||
.prv-chartbox.prv-noscript canvas{display:none;}
|
||||
/* chart legend */
|
||||
.prv-chartcap{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:8px 22px 4px;font-size:12.5px;color:#9AA7B2;}
|
||||
.prv-chartcap-item{display:inline-flex;align-items:center;gap:6px;}
|
||||
.prv-leg-solid{width:20px;height:0;border-top:3px solid #34C0CA;display:inline-block;}
|
||||
.prv-leg-dash{width:20px;height:0;border-top:2px dashed #B6F25A;display:inline-block;}
|
||||
.prv-leg-cfg{display:inline-flex;align-items:center;gap:3px;}
|
||||
.prv-leg-cfg::before{content:"";width:0;height:14px;border-left:2px dashed #FFB36E;display:inline-block;}
|
||||
.prv-leg-cfg::after{content:"";width:8px;height:8px;border-radius:50%;background:#FF9D4D;display:inline-block;}
|
||||
/* config change note */
|
||||
.prv-cfg-note{display:flex;gap:10px;align-items:flex-start;margin:8px 22px 14px;background:rgba(255,157,77,.15);border:1px solid rgba(255,157,77,.42);border-radius:8px;padding:11px 14px;color:#EEF2F5;font-size:13px;line-height:1.45;}
|
||||
.prv-cfg-note-icon{color:#FFB36E;font-style:normal;flex:0 0 16px;margin-top:1px;}
|
||||
.prv-cfg-note b{color:#FFB36E;}
|
||||
/* standings table */
|
||||
.prv-standings{margin:0;}
|
||||
.prv-status{display:inline-flex;align-items:center;gap:4px;font-size:12.5px;font-weight:600;padding:2px 8px;border-radius:999px;}
|
||||
.prv-status--cited{background:rgba(155,230,53,.15);color:#9BE635;}
|
||||
.prv-status--not-yet{background:rgba(136,147,160,.14);color:#B4BDC7;}
|
||||
/* proxy note */
|
||||
.prv-proxy-note{margin:10px 0;}
|
||||
';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user