Post-PR-#11 polish: demo UI, staffer console, face pool, icons, contractor profile (24 commits) #12

Merged
profit merged 44 commits from demo/post-pr11-polish-2026-04-28 into main 2026-05-03 05:16:17 +00:00
Showing only changes of commit cdf5f5926a - Show all commits

View File

@ -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);
});