From 855fafc298e3a643d2674b09116409f72e0fce4c Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Mon, 7 Sep 2026 19:46:30 -0400 Subject: [PATCH] fix(admin): rebalance profiles page hierarchy and add per-profile icons - promote definition key:value rows to card focus; demote admitted/ interactive counts to a compact header badge (gated-headline logic kept) - condense the locality coverage prose into a compact categories row with the full explanation on hover - add four feather-style SVG icons (clock, map-pin, dollar-sign, shield) and select per profile by dominant filter, falling back to the generic profile icon - swap the card grid to CSS auto-fill with a centered max-width so few profiles don't leave an unbalanced last row --- admin/frontend/profiles.html | 74 ++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/admin/frontend/profiles.html b/admin/frontend/profiles.html index 0cb6343..3b128f7 100644 --- a/admin/frontend/profiles.html +++ b/admin/frontend/profiles.html @@ -87,17 +87,26 @@ header.navbar{padding-top:2px!important;padding-bottom:2px!important} .navbar-brand a:hover .brand-speedlines line{animation:none} } /* ── Profile card specifics ── */ -.profile-stat{font-size:1.75rem;font-weight:700;line-height:1;font-variant-numeric:tabular-nums} -.profile-stat-label{font-size:.75rem;color:var(--tblr-secondary)} -.profile-field-label{color:var(--tblr-secondary);font-size:.75rem} -.profile-field-val{font-size:.78rem} +/* Admitted/interactive counts -- compact badge in card header */ +.profile-admit-badge{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:.82rem} +/* Promoted definition rows -- larger/brighter than the old .78rem muted styling */ +.profile-field-label{color:var(--tblr-secondary);font-size:.8rem;font-weight:600;letter-spacing:.01em} +.profile-field-val{color:rgba(255,255,255,.8);font-size:.92rem} .profile-zero-alert{margin-left:auto} -.profile-coverage{font-size:.72rem;color:var(--tblr-secondary);margin-bottom:.75rem;line-height:1.35} +/* Coverage note -- compact key:value row */ +.profile-coverage{font-size:.78rem;color:var(--tblr-secondary);margin-bottom:.5rem;line-height:1.3} +.profile-coverage .profile-field-label{font-size:.76rem;font-weight:500;letter-spacing:normal} +.profile-coverage .profile-field-val{font-size:.8rem;color:var(--tblr-secondary)} +.profile-coverage-info{display:inline-flex;align-items:center;margin-left:4px;opacity:.6;cursor:help;vertical-align:middle} +.profile-coverage-info:hover{opacity:1} .empty{color:var(--tblr-secondary);text-align:center;padding:2rem 1rem} /* ── Read-only lock hint on built-in cards ── */ .profile-readonly-hint{color:var(--tblr-secondary);font-size:.72rem;display:inline-flex;align-items:center;gap:4px} /* ── Card header actions ── */ .profile-actions{display:flex;align-items:center;gap:6px} +/* ── Profile grid: CSS auto-fill so 5 cards don't leave orphans ── */ +#profile-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:1rem;max-width:1200px;margin-left:auto!important;margin-right:auto!important} +#profile-grid>.col-12{grid-column:1/-1} /* ── Modal glass treatment ── */ .modal-content{ background: rgba(31,41,55,.75); @@ -317,10 +326,31 @@ function icon(name, size) { trash: ``, lock: ``, copy: ``, + clock: ``, + 'map-pin': ``, + 'dollar-sign': ``, + shield: ``, }; return svgs[name] || svgs.default; } +/* ── Select a per-profile card-header icon based on definition filters. + Precedence (first match wins): + 1. latency_tolerance set -> clock (time/latency) + 2. max_cost_per_1m_completion set -> dollar-sign (cost) + 3. min_tier / max_tier set -> shield (tier/scope) + 4. provider set OR category-gated -> map-pin (provider/locality) + 5. no non-null filters -> profile (generic default) ── */ +function profileIcon(def, cov) { + def = def || {}; + if (def.latency_tolerance != null) return 'clock'; + if (def.max_cost_per_1m_completion != null) return 'dollar-sign'; + if (def.min_tier != null || def.max_tier != null) return 'shield'; + if (def.provider != null) return 'map-pin'; + if (cov && cov.admitting > 0 && cov.admitting < cov.total) return 'map-pin'; + return 'profile'; +} + function renderStaticIcons() { document.querySelectorAll('[data-icon]').forEach(el => { el.innerHTML = icon(el.dataset.icon); @@ -475,16 +505,19 @@ function renderProfiles(profiles) { const headlineInteractive = gated ? Number(cov.max_interactive_admitted || 0) : interactive; const headlineModels = gated ? cov.models : p.admitted_models; + const admitBadge = `${headline.toLocaleString()} admitted · ${headlineInteractive.toLocaleString()} interactive`; + const zeroBadge = zeroAdmit ? `admits 0 models` : ''; const coverageNote = gated - ? `
- Category-gated: admits ${headline.toLocaleString()} - ${headline === 1 ? 'model' : 'models'} for - ${cov.admitting} of ${cov.total} task categories. - ${admitted.toLocaleString()} for an unspecified category — - its models name the categories they serve, so a category-less probe admits none of them. + ? `
+
categories
+
${cov.admitting} of ${cov.total} + + + +
` : ''; const readonlyHint = isBuiltin @@ -502,15 +535,16 @@ function renderProfiles(profiles) {
`; - return `
+ const cardIcon = profileIcon(p.definition, cov); + return `
- +

${escapeHtml(String(p.name || '—'))}

- ${zeroBadge} +
${admitBadge}${zeroBadge}
${sourceBadge(p.source)} ${readonlyHint}
@@ -518,18 +552,8 @@ function renderProfiles(profiles) {
-
-
-
${headline.toLocaleString()}
-
admitted models
-
-
-
${headlineInteractive.toLocaleString()}
-
interactive models
-
-
- ${coverageNote} ${formatDefinition(p.definition)} + ${coverageNote}
`; -- 2.49.1