Clean up metric breakdown panel — compact grid, top 8 with show-all toggle

Replaced the large flex-wrap button cards with a tight CSS grid of compact
cells (130px min). Each cell shows metric ID, current %, and NC count only.
Category text and target removed to reduce noise.

Capped to top 8 metrics by default with a 'Show all N' toggle for the rest.
Removes visual clutter while keeping the data accessible.
This commit is contained in:
Jordan Ramos
2026-05-14 15:29:20 -06:00
parent 7577ab1219
commit a72300475b

View File

@@ -100,55 +100,55 @@ function StatsBar({ stats, onNonCompliantClick, ncExpanded }) {
// ---------------------------------------------------------------------------
// Metric Breakdown Panel (shown when Non-Compliant is clicked)
// ---------------------------------------------------------------------------
function MetricBreakdownPanel({ metrics, onSelectMetric }) {
function MetricBreakdownPanel({ metrics }) {
const [showAll, setShowAll] = useState(false);
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;
const TOP_COUNT = 8;
const displayMetrics = showAll ? ncMetrics : ncMetrics.slice(0, TOP_COUNT);
const hasMore = ncMetrics.length > TOP_COUNT;
return (
<div style={{ ...CARD_STYLE, marginBottom: '1.5rem' }}>
<div style={{ fontSize: '0.7rem', color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '1rem' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '0.75rem' }}>
<div style={{ fontSize: '0.7rem', color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
Non-Compliant by Metric
</div>
<div style={{ display: 'flex', gap: '0.625rem', flexWrap: 'wrap' }}>
{ncMetrics.map(m => {
{hasMore && (
<button
onClick={() => setShowAll(!showAll)}
style={{ background: 'none', border: 'none', color: PURPLE, fontSize: '0.7rem', cursor: 'pointer', padding: '0.2rem 0.5rem' }}
>
{showAll ? 'Show top 8' : `Show all ${ncMetrics.length}`}
</button>
)}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(130px, 1fr))', gap: '0.5rem' }}>
{displayMetrics.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
<div
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',
background: 'rgba(15, 23, 42, 0.7)',
border: `1px solid ${color}30`,
borderRadius: '0.4rem',
padding: '0.6rem 0.75rem',
}}
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 style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: '0.3rem' }}>
<span style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: '700', color: '#E2E8F0' }}>{m.metric_id}</span>
<span style={{ fontSize: '0.55rem', color: '#475569', fontFamily: 'monospace' }}>{(pct * 100).toFixed(0)}%</span>
</div>
<div style={{ fontSize: '0.6rem', color: '#475569', textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: '0.5rem' }}>
{m.category}
<div style={{ fontSize: '1rem', fontWeight: '700', color }}>{m.non_compliant.toLocaleString()}</div>
</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>
@@ -801,7 +801,6 @@ function DataManagementPanel({ onClose, onDataChanged }) {
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem' }}>
<h2 style={{ fontSize: '1.1rem', fontWeight: '700', color: '#E2E8F0', margin: 0 }}>Manage Data</h2>
{/* ⚠️ CONVENTION: Use lucide-react <X /> icon instead of raw Unicode character */}
{/* ⚠️ CONVENTION: Use lucide-react <X /> icon instead of raw Unicode character */}
<button onClick={onClose} style={{ background: 'none', border: 'none', color: '#64748B', cursor: 'pointer' }}></button>
</div>
@@ -1052,14 +1051,7 @@ export default function CCPMetricsPage() {
{/* 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);
}}
/>
<MetricBreakdownPanel metrics={stats.metric_breakdown} />
)}
{/* Charts row */}