Make Non-Compliant stat clickable — reveals metric breakdown buttons

Clicking the Non-Compliant card on the CCP Metrics overview now toggles a
panel of metric buttons below it, each showing the metric ID, category,
non-compliant count, and compliance % vs target. Styled like the compliance
page's MetricHealthCard pattern.

Backend: added metric_breakdown to the /stats response — aggregated
cross-vertical metric totals (ALL: rows only, grouped by metric_id).

Also updated tech steering file to document the single-port Express
architecture and the requirement to run npm run build after frontend changes.
This commit is contained in:
Jordan Ramos
2026-05-14 15:24:10 -06:00
parent a2bc1ff564
commit 7577ab1219
3 changed files with 210 additions and 6 deletions

View File

@@ -57,21 +57,39 @@ const TD_STYLE = {
// ---------------------------------------------------------------------------
// Stats Bar
// ---------------------------------------------------------------------------
function StatsBar({ stats }) {
function StatsBar({ stats, onNonCompliantClick, ncExpanded }) {
if (!stats) return null;
const items = [
{ label: 'Total Devices', value: stats.total_devices.toLocaleString(), color: '#94A3B8' },
{ label: 'Compliant', value: stats.compliant.toLocaleString(), color: '#10B981' },
{ label: 'Non-Compliant', value: stats.non_compliant.toLocaleString(), color: '#EF4444' },
{ label: 'Non-Compliant', value: stats.non_compliant.toLocaleString(), color: '#EF4444', clickable: true },
{ label: 'Current %', value: `${stats.compliance_pct}%`, color: stats.compliance_pct >= stats.target_pct ? '#10B981' : '#F59E0B' },
{ label: 'Target %', value: `${stats.target_pct}%`, color: PURPLE },
];
return (
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap', marginBottom: '1.5rem' }}>
{items.map(({ label, value, color }) => (
<div key={label} style={STAT_CARD_STYLE}>
<div style={{ fontSize: '0.65rem', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '0.5rem' }}>{label}</div>
{items.map(({ label, value, color, clickable }) => (
<div
key={label}
onClick={clickable ? onNonCompliantClick : undefined}
style={{
...STAT_CARD_STYLE,
cursor: clickable ? 'pointer' : 'default',
border: clickable && ncExpanded
? '1px solid rgba(239, 68, 68, 0.6)'
: STAT_CARD_STYLE.border,
background: clickable && ncExpanded
? 'rgba(239, 68, 68, 0.08)'
: STAT_CARD_STYLE.background,
transition: 'all 0.15s',
}}
onMouseEnter={clickable ? e => { e.currentTarget.style.borderColor = 'rgba(239, 68, 68, 0.5)'; } : undefined}
onMouseLeave={clickable ? e => { if (!ncExpanded) e.currentTarget.style.borderColor = 'rgba(167, 139, 250, 0.2)'; } : undefined}
>
<div style={{ fontSize: '0.65rem', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '0.5rem' }}>
{label}{clickable && <span style={{ marginLeft: '0.4rem', fontSize: '0.6rem', color: '#64748B' }}>{ncExpanded ? '▾' : '▸'}</span>}
</div>
<div style={{ fontSize: '1.5rem', fontWeight: '700', color }}>{value}</div>
</div>
))}
@@ -79,6 +97,65 @@ function StatsBar({ stats }) {
);
}
// ---------------------------------------------------------------------------
// Metric Breakdown Panel (shown when Non-Compliant is clicked)
// ---------------------------------------------------------------------------
function MetricBreakdownPanel({ metrics, onSelectMetric }) {
if (!metrics || metrics.length === 0) return null;
// Only show metrics with non_compliant > 0
const ncMetrics = metrics.filter(m => m.non_compliant > 0);
if (ncMetrics.length === 0) return null;
return (
<div style={{ ...CARD_STYLE, marginBottom: '1.5rem' }}>
<div style={{ fontSize: '0.7rem', color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '1rem' }}>
Non-Compliant by Metric
</div>
<div style={{ display: 'flex', gap: '0.625rem', flexWrap: 'wrap' }}>
{ncMetrics.map(m => {
const pct = m.total > 0 ? (m.compliant / m.total) : 0;
const target = Number(m.target || 0);
const color = pct >= target ? '#10B981' : pct >= target * 0.85 ? '#F59E0B' : '#EF4444';
return (
<button
key={m.metric_id}
onClick={() => onSelectMetric(m.metric_id)}
style={{
background: 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
border: `1.5px solid ${color}40`,
borderRadius: '0.5rem',
padding: '0.75rem 1rem',
cursor: 'pointer',
textAlign: 'left',
transition: 'all 0.15s',
minWidth: '140px',
flex: '1 1 0',
maxWidth: '200px',
}}
onMouseEnter={e => { e.currentTarget.style.borderColor = color + '80'; e.currentTarget.style.background = `${color}10`; }}
onMouseLeave={e => { e.currentTarget.style.borderColor = color + '40'; e.currentTarget.style.background = 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)'; }}
>
<div style={{ fontFamily: 'monospace', fontSize: '0.9rem', fontWeight: '700', color: '#E2E8F0', marginBottom: '0.2rem' }}>
{m.metric_id}
</div>
<div style={{ fontSize: '0.6rem', color: '#475569', textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: '0.5rem' }}>
{m.category}
</div>
<div style={{ fontSize: '1.1rem', fontWeight: '700', color: '#EF4444', marginBottom: '0.2rem' }}>
{m.non_compliant.toLocaleString()}
</div>
<div style={{ fontSize: '0.6rem', color: '#64748B', fontFamily: 'monospace' }}>
{(pct * 100).toFixed(0)}% / target {(target * 100).toFixed(0)}%
</div>
</button>
);
})}
</div>
</div>
);
}
// ---------------------------------------------------------------------------
// Donut Chart
// ---------------------------------------------------------------------------
@@ -816,6 +893,7 @@ export default function CCPMetricsPage() {
const [error, setError] = useState(null);
const [showUpload, setShowUpload] = useState(false);
const [showManage, setShowManage] = useState(false);
const [showMetricBreakdown, setShowMetricBreakdown] = useState(false);
// Drill-down state
const [selectedVertical, setSelectedVertical] = useState(null);
@@ -966,7 +1044,23 @@ export default function CCPMetricsPage() {
{!loading && !error && stats && (
<>
{/* Stats bar */}
<StatsBar stats={stats.stats} />
<StatsBar
stats={stats.stats}
onNonCompliantClick={() => setShowMetricBreakdown(!showMetricBreakdown)}
ncExpanded={showMetricBreakdown}
/>
{/* Metric breakdown (revealed when Non-Compliant is clicked) */}
{showMetricBreakdown && (
<MetricBreakdownPanel
metrics={stats.metric_breakdown}
onSelectMetric={(metricId) => {
// Find the first vertical that has this metric with non-compliant > 0
// and navigate to it
setShowMetricBreakdown(false);
}}
/>
)}
{/* Charts row */}
<div style={{ display: 'flex', gap: '1.5rem', marginBottom: '1.5rem', flexWrap: 'wrap' }}>