Redesign Breadcrumbs panel: list + detail split view (option 3)

This commit is contained in:
2026-08-24 16:09:08 -07:00
parent e30ef72769
commit 3df15b3520

View File

@@ -124,6 +124,19 @@
.bc-related summary{cursor:pointer; color:var(--interactive);}
.bc-related li{margin-left:22px; padding:2px 0; color:var(--text-secondary);}
.bc-foot{display:flex; align-items:center; gap:14px; margin-top:12px; flex-wrap:wrap;}
.bc-layout{display:grid; grid-template-columns:250px 1fr; gap:16px; align-items:start;}
.bc-nav{background:var(--layer); border:1px solid var(--border); position:sticky; top:12px; max-height:80vh; overflow-y:auto;}
.bc-nav-item{display:flex; align-items:center; gap:9px; padding:10px 12px; border-bottom:1px solid var(--border); cursor:pointer;}
.bc-nav-item:last-child{border-bottom:none;}
.bc-nav-item:hover{background:var(--layer-hover);}
.bc-nav-item.active{background:var(--layer-hover); border-left:3px solid var(--interactive); padding-left:9px;}
.bc-nav-text{flex:1; font-size:12.5px; line-height:1.35; color:var(--text-secondary);}
.bc-nav-item.active .bc-nav-text{color:var(--text); font-weight:600;}
.bc-dot{width:8px; height:8px; border-radius:50%; flex-shrink:0; background:var(--border-strong);}
.bc-dot.complete{background:var(--success);}
.bc-dot.partial{background:var(--warning);}
.bc-detail{min-width:0;}
@media (max-width:900px){ .bc-layout{grid-template-columns:1fr;} .bc-nav{position:static; max-height:none;} }
.gate{display:flex; gap:12px; font-size:12.5px; flex-wrap:wrap;}
.gate span{color:var(--text-helper);}
.gate span.ok{color:var(--success); font-weight:600;}
@@ -205,7 +218,10 @@
<p>Root problem to value driver to capability to measurable outcome. A completed breadcrumb IS a pilot success criterion; the export builds the criteria table from every complete card. AI draft fills the fields from construction execution methodology (EVM, CII AWP, Last Planner, first-time quality); the room reviews and edits.</p>
</div>
<div class="summary-bar" id="bcSummary"></div>
<div id="bcList"></div>
<div class="bc-layout">
<nav class="bc-nav" id="bcNav"></nav>
<div class="bc-detail" id="bcDetail"></div>
</div>
<div class="add-row"><button class="btn ghost" id="btnAddBc">Add a blank breadcrumb</button></div>
</section>
@@ -478,7 +494,9 @@ document.getElementById('panel-map').addEventListener('click', e=>{
const c = clusterById(t.dataset.cprom);
if(c){
c.promoted = true;
S.breadcrumbs.push(B({prob:(c.root||c.name), related:activeMembers(c.id).map(p=>p.t)}));
const nb = B({prob:(c.root||c.name), related:activeMembers(c.id).map(p=>p.t)});
S.breadcrumbs.push(nb);
bcSelectedId = nb.id;
save(); renderAll(); toast('Promoted with ' + activeMembers(c.id).length + ' related issues');
}
return;
@@ -567,6 +585,8 @@ document.getElementById('newCluster').addEventListener('keydown', e=>{ if(e.key=
const ADOPTION_WORDS = /implement|adopt|roll ?out|have a tool|tool exists|usage|active users?|users? active/i;
const REQ = ['driver','cap','metric','method','baseline'];
function bcComplete(b){ return REQ.every(f=>String(b[f]||'').trim().length>0); }
function bcStatus(b){ if(bcComplete(b)) return 'complete'; if(REQ.some(f=>String(b[f]||'').trim())) return 'partial'; return 'blank'; }
let bcSelectedId = null;
function footHTML(b){
const adoptionFlag = b.type==='Outcome' && ADOPTION_WORDS.test(b.metric||'');
const gate = REQ.map(f=>{
@@ -587,12 +607,7 @@ function bcSummaryHTML(){
<div class="${nInc?'warn':''}"><b>${nInc}</b>Incomplete</div>
<div><b>${nOut}</b>Outcome</div><div><b>${S.breadcrumbs.length-nOut}</b>Adoption</div>`;
}
function renderBc(){
const wrap = document.getElementById('bcList');
document.getElementById('cntBc').textContent = S.breadcrumbs.length;
document.getElementById('bcSummary').innerHTML = bcSummaryHTML();
if(!S.breadcrumbs.length){ wrap.innerHTML = '<div class="empty">Promote root problems from the map, or add a blank breadcrumb.</div>'; return; }
wrap.innerHTML = S.breadcrumbs.map(b=>{
function bcCardHTML(b){
const rel = (b.related && b.related.length)
? `<details class="bc-related"><summary>Related issues carried from the cluster (${b.related.length})</summary><ul>${b.related.map(r=>`<li>${esc(r)}</li>`).join('')}</ul></details>` : '';
return `<div class="bc${b.example?' example':''}">
@@ -620,20 +635,48 @@ function renderBc(){
</div>
<div class="bc-foot" data-foot="${b.id}">${footHTML(b)}</div>
</div>`;
}).join('');
}
document.getElementById('bcList').addEventListener('input', e=>{
function bcNavRowHTML(b){
const label = shortT(b.prob, 60) || '(blank breadcrumb)';
return `<div class="bc-nav-item${b.id===bcSelectedId?' active':''}" data-bcnav="${b.id}">
<span class="bc-dot ${bcStatus(b)}"></span>
<span class="bc-nav-text">${esc(label)}</span>
</div>`;
}
function renderBc(){
document.getElementById('cntBc').textContent = S.breadcrumbs.length;
document.getElementById('bcSummary').innerHTML = bcSummaryHTML();
const nav = document.getElementById('bcNav');
const detail = document.getElementById('bcDetail');
if(!S.breadcrumbs.length){
nav.innerHTML = '';
detail.innerHTML = '<div class="empty">Promote root problems from the map, or add a blank breadcrumb.</div>';
bcSelectedId = null;
return;
}
if(!S.breadcrumbs.some(b=>b.id===bcSelectedId)) bcSelectedId = S.breadcrumbs[0].id;
nav.innerHTML = S.breadcrumbs.map(bcNavRowHTML).join('');
const selected = S.breadcrumbs.find(b=>b.id===bcSelectedId);
detail.innerHTML = bcCardHTML(selected);
}
document.getElementById('bcNav').addEventListener('click', e=>{
const row = e.target.closest('[data-bcnav]');
if(row){ bcSelectedId = row.dataset.bcnav; renderBc(); }
});
document.getElementById('bcDetail').addEventListener('input', e=>{
if(e.target.dataset.bid){
const b = S.breadcrumbs.find(x=>x.id===e.target.dataset.bid);
if(b){
b[e.target.dataset.f] = e.target.value; save();
const foot = document.querySelector(`[data-foot="${b.id}"]`);
if(foot) foot.innerHTML = footHTML(b);
const navRow = document.querySelector(`[data-bcnav="${b.id}"]`);
if(navRow) navRow.outerHTML = bcNavRowHTML(b);
document.getElementById('bcSummary').innerHTML = bcSummaryHTML();
}
}
});
document.getElementById('bcList').addEventListener('click', e=>{
document.getElementById('bcDetail').addEventListener('click', e=>{
const tt = e.target.closest('[data-tt]');
if(tt){
const b = S.breadcrumbs.find(x=>x.id===tt.dataset.tt);
@@ -647,7 +690,9 @@ document.getElementById('bcList').addEventListener('click', e=>{
}
});
document.getElementById('btnAddBc').addEventListener('click', ()=>{
S.breadcrumbs.push(B({}));
const nb = B({});
S.breadcrumbs.push(nb);
bcSelectedId = nb.id;
save(); renderBc();
});