From cdf5f5926a7810e564045d7ed10ab7181932b98e Mon Sep 17 00:00:00 2001 From: root Date: Mon, 27 Apr 2026 23:47:12 -0500 Subject: [PATCH] =?UTF-8?q?demo:=20console=20=E2=80=94=20sober=20worker=20?= =?UTF-8?q?cards=20(mirror=20dashboard=20styling)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit J: "can you update Staffer's Console too the same look." Console rendered worker rows in three places (Chapter 4 permit-contract candidates, Chapter 8 triage backfills, Chapter 9 try-it-yourself results) with the original 28px square avatar + flat backgrounds — inconsistent with the new dashboard design. Three changes: 1. CSS — .worker now has a 3px left-edge border that color-codes the role family, and .av is a 32px circle with a muted dark background + 1px ring + monogram initials. Five role-band colors mirror search.html: warehouse blue / production amber / trades purple / driver green / lead orange. Plus a .role-pill style matching the dashboard's small uppercase chip. 2. Helpers — added ROLE_BANDS regex table + roleBand() classifier and a new workerRow(name, role, detail, opts) builder. Same regex patterns as search.html so a "Forklift Operator" classifies identically on every page. opts.endorsed adds the green endorsed chip; opts.score appends a rank badge. 3. Replaced the three inline avatar+row constructors with workerRow() calls. Net: console.html lost ~20 lines of duplicated DOM building while gaining role bands + pills. Verified end-to-end via playwright on devop.live/lakehouse/console: Chapter 8 triage scenario "Marcus running late site 4422": 5 backfill rows render with [warehouse] band + WAREHOUSE pill + monogram avatars (SBC, ETW, SHC, WMG, MEB). Same sober look as the dashboard worker cards. No emojis, no cartoons, color-coded role family on the left edge. --- mcp-server/console.html | 111 ++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 34 deletions(-) diff --git a/mcp-server/console.html b/mcp-server/console.html index f75b293..38e0ef6 100644 --- a/mcp-server/console.html +++ b/mcp-server/console.html @@ -54,8 +54,19 @@ details .body{padding-top:10px;font-size:12px;color:#8b949e} .accent-g{border-left:3px solid #3fb950} .accent-r{border-left:3px solid #f85149} -.worker{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px} -.worker .av{width:28px;height:28px;border-radius:6px;background:#1a2744;display:flex;align-items:center;justify-content:center;font-weight:600;color:#e6edf3;font-size:10px;flex-shrink:0} +.worker{display:flex;align-items:center;gap:10px;padding:8px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px;border-left:3px solid #30363d} +.worker .av{width:32px;height:32px;border-radius:50%;background:#0d1117;border:1px solid #21262d;display:flex;align-items:center;justify-content:center;font-weight:600;color:#c9d1d9;font-size:11px;flex-shrink:0;letter-spacing:0.5px} +.worker[data-role-band="warehouse"]{border-left-color:#58a6ff} +.worker[data-role-band="production"]{border-left-color:#d29922} +.worker[data-role-band="trades"]{border-left-color:#bc8cff} +.worker[data-role-band="driver"]{border-left-color:#3fb950} +.worker[data-role-band="lead"]{border-left-color:#f0883e} +.role-pill{display:inline-block;font-size:9px;padding:1px 7px;border-radius:3px;background:#0d1117;color:#8b949e;margin-right:6px;font-weight:600;letter-spacing:0.4px;text-transform:uppercase;border-left:2px solid #30363d;vertical-align:1px} +.role-pill[data-rb="warehouse"]{border-left-color:#58a6ff;color:#79c0ff} +.role-pill[data-rb="production"]{border-left-color:#d29922;color:#e3b341} +.role-pill[data-rb="trades"]{border-left-color:#bc8cff;color:#d2a8ff} +.role-pill[data-rb="driver"]{border-left-color:#3fb950;color:#56d364} +.role-pill[data-rb="lead"]{border-left-color:#f0883e;color:#ffa657} .worker .info{flex:1;min-width:0} .worker .nm{color:#e6edf3;font-weight:500} .worker .why{color:#545d68;font-size:11px;margin-top:1px} @@ -199,6 +210,57 @@ var A=location.origin+P; // DOM helpers — all dynamic content goes through these. No innerHTML // anywhere in the script; every API-derived string passes through // textContent so no injection path regardless of upstream data. +// Role classification — mirrors search.html, no emojis. Maps role +// strings to a band+label used by the worker-card border + role pill. +var ROLE_BANDS = [ + { match: /forklift|warehouse|associate|material\s*handler|loader|loading|packag|shipping|logistics|inventory|sanitation|janit/i, band: 'warehouse', label: 'Warehouse' }, + { match: /production|assembl/i, band: 'production', label: 'Production' }, + { match: /welder|weld|electric|maint(enance)?\s*tech|cnc|machine\s*op|hvac|plumb|carpenter|mason/i, band: 'trades', label: 'Skilled Trade' }, + { match: /driver|truck|haul|cdl/i, band: 'driver', label: 'Driver' }, + { match: /line\s*lead|supervisor|foreman|coordinator/i, band: 'lead', label: 'Lead' }, + { match: /quality/i, band: 'production', label: 'Quality' }, +]; +function roleBand(role){ + if(!role) return { band: 'warehouse', label: '' }; + for (var i = 0; i < ROLE_BANDS.length; i++) { + if (ROLE_BANDS[i].match.test(role)) return ROLE_BANDS[i]; + } + return { band: 'warehouse', label: role.split(' ')[0].toUpperCase().slice(0, 12) }; +} + +// Build a sober worker card: monogram avatar + colored role band on +// the left edge + uppercase role pill in the detail line. Used by +// every chapter that renders worker rows. `name` and `role` drive the +// classification; `detail` is the full text after the pill. +function workerRow(name, role, detail, opts){ + opts = opts || {}; + var band = roleBand(role||''); + var w = el('div','worker'); + if(band.band) w.dataset.roleBand = band.band; + var initials = (name||'?').split(' ').map(function(s){return (s[0]||'').toUpperCase()}).join('').substring(0,2); + w.appendChild(el('div','av',initials)); + var info = el('div','info'); + var nm = el('div','nm', name||'?'); + if(opts.endorsed){ + nm.appendChild(el('span','boost-chip',opts.endorsed)); + } + info.appendChild(nm); + var why = el('div','why'); + if(band.label){ + var pill = document.createElement('span'); pill.className='role-pill'; + pill.dataset.rb = band.band; + pill.textContent = band.label; + why.appendChild(pill); + } + why.appendChild(document.createTextNode(detail||'')); + info.appendChild(why); + w.appendChild(info); + if(opts.score){ + w.appendChild(el('div','score', opts.score)); + } + return w; +} + function el(tag, cls, text){ var e=document.createElement(tag); if(cls) e.className=cls; @@ -380,21 +442,13 @@ function loadChapter4(){ var list=document.createElement('div');list.style.marginTop='6px'; (prop.candidates||[]).slice(0,5).forEach(function(cand,i){ - var w=el('div','worker'); - var initials=(cand.name||'?').split(' ').map(function(s){return (s[0]||'').toUpperCase()}).join('').substring(0,2); - w.appendChild(el('div','av',initials)); - var info=el('div','info'); - var nm=el('div','nm',cand.name||cand.doc_id||'?'); - if((cand.playbook_boost||0)>0){ - var ncit=(cand.playbook_citations||[]).length; - nm.appendChild(el('span','boost-chip','Endorsed · '+ncit+' past fill'+(ncit!==1?'s':''))); - } - info.appendChild(nm); - var why=cand.doc_id+' · '+(cand.playbook_boost>0?'boosted +'+cand.playbook_boost.toFixed(3)+' by memory · ':'')+'semantic score '+(cand.score||0).toFixed(3); - info.appendChild(el('div','why',why)); - w.appendChild(info); - w.appendChild(el('div','score','#'+(i+1))); - list.appendChild(w); + var detail = cand.doc_id+' · '+(cand.playbook_boost>0?'boosted +'+cand.playbook_boost.toFixed(3)+' by memory · ':'')+'semantic score '+(cand.score||0).toFixed(3); + var endorsed = (cand.playbook_boost||0) > 0 + ? 'Endorsed · '+((cand.playbook_citations||[]).length)+' past fill'+((cand.playbook_citations||[]).length!==1?'s':'') + : null; + list.appendChild(workerRow(cand.name||cand.doc_id||'?', prop.role||'', detail, { + endorsed: endorsed, score: '#'+(i+1) + })); }); card.appendChild(list); @@ -628,12 +682,8 @@ function loadChapter8(){ bfHdr.textContent='✓ '+d.backfills.length+' local '+(d.worker.role||'workers')+' available — sorted by responsiveness'; host.appendChild(bfHdr); d.backfills.slice(0,5).forEach(function(c){ - var row=el('div','row'); - var left=document.createElement('div');left.style.flex='1';left.style.minWidth='0'; - left.appendChild(el('div','title',c.name)); - left.appendChild(el('div','meta',(c.role||'?')+' · '+(c.city||'')+', '+(c.state||'')+' · rel '+Math.round((c.rel||0)*100)+'% · resp '+Math.round((c.resp||0)*100)+'%')); - row.appendChild(left); - host.appendChild(row); + var detail=(c.role||'?')+' · '+(c.city||'')+', '+(c.state||'')+' · rel '+Math.round((c.rel||0)*100)+'% · resp '+Math.round((c.resp||0)*100)+'%'; + host.appendChild(workerRow(c.name||'?', c.role||'', detail)); }); } var narr=el('div','narr'); @@ -675,23 +725,16 @@ function runTry(){ var workers=d.sql_results||d.vector_results||d.results||[]; workers.slice(0,5).forEach(function(w,i){ - var row=el('div','worker'); var nm=w.name||(w.text||'').split('—')[0].trim()||w.doc_id||'?'; - var initials=nm.split(' ').map(function(s){return (s[0]||'').toUpperCase()}).join('').substring(0,2); - row.appendChild(el('div','av',initials)); - var info=el('div','info'); - var n=el('div','nm',nm); - if((w.playbook_boost||0)>0){ - n.appendChild(el('span','boost-chip','Endorsed · '+((w.playbook_citations||[]).length||'?')+' past fill(s)')); - } - info.appendChild(n); var bits=[]; if(w.role) bits.push(w.role); if(w.city&&w.state) bits.push(w.city+', '+w.state); if(w.rel!==undefined) bits.push('reliability '+Math.round(w.rel*100)+'%'); if(w.avail!==undefined) bits.push('availability '+Math.round(w.avail*100)+'%'); - info.appendChild(el('div','why',bits.join(' · ')||'AI semantic match')); - row.appendChild(info); + var endorsed = (w.playbook_boost||0) > 0 + ? 'Endorsed · '+((w.playbook_citations||[]).length||'?')+' past fill(s)' + : null; + var row = workerRow(nm, w.role||'', bits.join(' · ')||'AI semantic match', { endorsed: endorsed }); row.appendChild(el('div','score','#'+(i+1))); card.appendChild(row); });