Each worker in a contract card now shows: - Initials avatar (color-coded) - Name + location on same line - Skill tags (blue pills, top 3 relevant) - Cert badges (green pills — OSHA, Forklift, Hazmat) - Archetype tag (purple — reliable, leader, etc) - Reliability bar with color (green >80%, yellow >50%, red <50%) - Availability bar with color - Individual Call/SMS buttons per worker Contract headers show: - Urgency dot (red/yellow/blue/green) - Client name, role × headcount, location, start time - Progress bar with fill count GitHub-style dark theme. Every piece of info visible at a glance without clicking anything. The staffer sees skills, certs, and reliability for every matched worker the moment the page loads. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
335 lines
18 KiB
HTML
335 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html><head>
|
||
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Staffing Co-Pilot</title>
|
||
<style>
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;background:#0b0f19;color:#c9d1d9;font-size:13px;-webkit-font-smoothing:antialiased}
|
||
|
||
/* Top bar */
|
||
.bar{background:#161b22;padding:10px 20px;border-bottom:1px solid #21262d;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px}
|
||
.bar h1{font-size:15px;font-weight:600;color:#f0f6fc}
|
||
.bar h1 span{color:#7c3aed}
|
||
.bar .right{display:flex;gap:12px;align-items:center;font-size:11px;color:#8b949e}
|
||
|
||
/* Pipeline counters */
|
||
.pipeline{display:flex;gap:1px;margin:16px 16px 0;background:#161b22;border-radius:10px;overflow:hidden;border:1px solid #21262d}
|
||
.pip{flex:1;padding:14px 8px;text-align:center}
|
||
.pip .n{font-size:28px;font-weight:800;font-variant-numeric:tabular-nums}
|
||
.pip .l{font-size:9px;text-transform:uppercase;letter-spacing:1px;color:#8b949e;margin-top:2px}
|
||
.pip.red .n{color:#f85149}.pip.yel .n{color:#d29922}.pip.blu .n{color:#58a6ff}.pip.grn .n{color:#3fb950}
|
||
|
||
/* Content */
|
||
.content{padding:16px;max-width:1200px;margin:0 auto}
|
||
.section-label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:1px;font-weight:600;margin:20px 0 10px;display:flex;justify-content:space-between;align-items:center}
|
||
|
||
/* Contract card */
|
||
.contract{background:#161b22;border:1px solid #21262d;border-radius:10px;margin-bottom:14px;overflow:hidden}
|
||
.contract .hdr{padding:14px 16px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #21262d;flex-wrap:wrap;gap:8px}
|
||
.contract .hdr .left{display:flex;align-items:center;gap:10px}
|
||
.contract .urgency{width:10px;height:10px;border-radius:50%;flex-shrink:0}
|
||
.urgency.urgent{background:#f85149}.urgency.high{background:#d29922}.urgency.medium{background:#58a6ff}.urgency.low{background:#3fb950}.urgency.filled{background:#3fb950}
|
||
.contract .client{font-size:15px;font-weight:600;color:#f0f6fc}
|
||
.contract .need{font-size:12px;color:#8b949e}
|
||
.contract .hdr .right{display:flex;align-items:center;gap:8px}
|
||
.progress{background:#21262d;border-radius:10px;height:6px;width:80px;overflow:hidden}
|
||
.progress .fill{height:100%;border-radius:10px;transition:width .3s}
|
||
.fill.full{background:#3fb950}.fill.partial{background:#d29922}.fill.empty{background:#484f58}
|
||
.contract .hdr .count{font-size:12px;font-weight:600;color:#f0f6fc}
|
||
|
||
/* Worker card inside contract */
|
||
.workers{padding:6px}
|
||
.worker{display:flex;align-items:flex-start;gap:12px;padding:10px 12px;border-radius:8px;transition:background .15s}
|
||
.worker:hover{background:#1c2333}
|
||
.worker .avatar{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;color:#f0f6fc;flex-shrink:0}
|
||
.avatar.a1{background:#1a3a2a}.avatar.a2{background:#1a2a3a}.avatar.a3{background:#2a1a3a}.avatar.a4{background:#3a2a1a}.avatar.a5{background:#1a3a3a}
|
||
.worker .info{flex:1;min-width:0}
|
||
.worker .name-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
||
.worker .wname{font-weight:600;color:#f0f6fc;font-size:13px}
|
||
.worker .wloc{color:#8b949e;font-size:11px}
|
||
.worker .tags{display:flex;gap:4px;flex-wrap:wrap;margin-top:4px}
|
||
.tag{padding:1px 7px;border-radius:10px;font-size:10px;font-weight:500}
|
||
.tag.skill{background:#1a2744;color:#58a6ff;border:1px solid #1f3d68}
|
||
.tag.cert{background:#1a3a2a;color:#3fb950;border:1px solid #238636}
|
||
.tag.type{background:#2a1a3a;color:#bc8cff;border:1px solid #553098}
|
||
.worker .metrics{display:flex;gap:12px;margin-top:6px;align-items:center}
|
||
.metric{display:flex;align-items:center;gap:4px}
|
||
.metric .label{color:#8b949e;font-size:10px}
|
||
.metric .bar{width:48px;height:4px;background:#21262d;border-radius:2px;overflow:hidden}
|
||
.metric .bar .val{height:100%;border-radius:2px}
|
||
.bar .high{background:#3fb950}.bar .med{background:#d29922}.bar .low{background:#f85149}
|
||
.metric .pct{font-size:10px;font-weight:600;font-variant-numeric:tabular-nums}
|
||
.worker .actions{display:flex;gap:4px;flex-shrink:0;align-self:center}
|
||
.wbtn{padding:4px 10px;border-radius:4px;font-size:10px;cursor:pointer;border:1px solid #21262d;background:#161b22;color:#8b949e;font-weight:500}
|
||
.wbtn:hover{border-color:#58a6ff;color:#58a6ff}
|
||
.wbtn.primary{background:#1f3d68;color:#58a6ff;border-color:#1f3d68}
|
||
|
||
/* Alerts strip */
|
||
.alerts{display:flex;gap:8px;margin:16px 16px 0;flex-wrap:wrap}
|
||
.alrt{padding:6px 12px;border-radius:6px;font-size:11px;display:flex;align-items:center;gap:4px}
|
||
.alrt.w{background:#2d1b00;color:#d29922;border:1px solid #533d13}
|
||
.alrt.i{background:#0d1d33;color:#58a6ff;border:1px solid #1f3d68}
|
||
.alrt.g{background:#0d261a;color:#3fb950;border:1px solid #238636}
|
||
|
||
/* Search */
|
||
.search-area{margin-top:20px;background:#161b22;border:1px solid #21262d;border-radius:10px;padding:14px}
|
||
.search-area summary{cursor:pointer;color:#8b949e;font-size:12px;list-style:none;display:flex;align-items:center;gap:6px}
|
||
.search-area summary::-webkit-details-marker{display:none}
|
||
.search-area[open] summary{margin-bottom:10px}
|
||
.search-area input[type=text]{width:100%;padding:10px 14px;background:#0b0f19;border:1px solid #21262d;border-radius:6px;color:#f0f6fc;font-size:13px;outline:none;margin-bottom:8px}
|
||
.search-area input:focus{border-color:#58a6ff}
|
||
.srow{display:flex;gap:6px;margin-bottom:8px}
|
||
.search-area select{flex:1;padding:7px;background:#0b0f19;border:1px solid #21262d;border-radius:4px;color:#c9d1d9;font-size:11px}
|
||
.sbtn{width:100%;padding:8px;background:#7c3aed;border:none;border-radius:6px;color:#fff;font-size:12px;font-weight:600;cursor:pointer}
|
||
.sbtn:hover{background:#6d28d9}
|
||
#sresults{margin-top:10px}
|
||
|
||
.footer{text-align:center;padding:16px;color:#484f58;font-size:11px;margin-top:20px}
|
||
.footer a{color:#58a6ff;text-decoration:none}
|
||
.loading{color:#484f58;text-align:center;padding:16px}
|
||
|
||
@media(max-width:768px){
|
||
.pipeline{flex-wrap:wrap}.pip{min-width:25%}
|
||
.worker{flex-direction:column}.worker .actions{align-self:flex-start}
|
||
.alerts{flex-direction:column}
|
||
.contract .hdr{flex-direction:column;align-items:flex-start}
|
||
}
|
||
</style></head><body>
|
||
<div class="bar">
|
||
<h1><span>●</span> Staffing Co-Pilot</h1>
|
||
<div class="right"><span id="status">Loading...</span></div>
|
||
</div>
|
||
<div class="pipeline" id="pipeline"></div>
|
||
<div class="alerts" id="alerts"></div>
|
||
<div class="content">
|
||
<div class="section-label"><span>Your Contracts</span><span id="day-label"></span></div>
|
||
<div id="contracts"><div class="loading">Loading your day...</div></div>
|
||
|
||
<details class="search-area">
|
||
<summary>🔍 Search all 500,000 workers</summary>
|
||
<input type="text" id="sq" placeholder="Type what you need..." onkeydown="if(event.key==='Enter')doSearch()">
|
||
<div class="srow">
|
||
<select id="sst"><option value="">Any State</option><option>IL</option><option>IN</option><option>OH</option><option>MO</option><option>TN</option><option>KY</option><option>WI</option><option>MI</option></select>
|
||
<select id="srl"><option value="">Any Role</option><option>Forklift Operator</option><option>Machine Operator</option><option>Assembler</option><option>Loader</option><option>Quality Tech</option><option>Welder</option><option>Sanitation Worker</option><option>Maintenance Tech</option></select>
|
||
</div>
|
||
<button class="sbtn" onclick="doSearch()">Search</button>
|
||
<div id="sresults"></div>
|
||
</details>
|
||
|
||
<div class="footer"><a href="proof">View Proof of Work</a> · 500K workers · 673K AI-indexed</div>
|
||
</div>
|
||
|
||
<script>
|
||
var P = location.pathname.indexOf('/lakehouse') >= 0 ? '/lakehouse' : '';
|
||
var A = location.origin + P;
|
||
var colors = ['a1','a2','a3','a4','a5'];
|
||
|
||
window.addEventListener('load', loadDay);
|
||
|
||
function loadDay() {
|
||
fetch(A + '/simulation/run', { method: 'POST', headers: {'Content-Type':'application/json'} })
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(d) {
|
||
var today = d.days ? d.days[0] : null;
|
||
var sum = d.summary || {};
|
||
renderPipeline(today);
|
||
renderContracts(today);
|
||
document.getElementById('status').textContent = sum.total_filled + '/' + sum.total_needed + ' filled · ' + sum.emergencies + ' urgent';
|
||
document.getElementById('day-label').textContent = today ? today.label + ' · ' + today.staffer : '';
|
||
})
|
||
.catch(function(e) { document.getElementById('contracts').textContent = 'Error: ' + e.message; });
|
||
|
||
// Alerts
|
||
fetch(A + '/sql', { method:'POST', headers:{'Content-Type':'application/json'},
|
||
body: JSON.stringify({sql:"SELECT archetype, COUNT(*) cnt FROM ethereal_workers WHERE archetype IN ('erratic','silent') GROUP BY archetype"})
|
||
}).then(function(r){return r.json()}).then(function(d){
|
||
var el = document.getElementById('alerts');
|
||
el.textContent = '';
|
||
(d.rows||[]).forEach(function(r){
|
||
var a=document.createElement('div');
|
||
a.className='alrt '+(r.archetype==='erratic'?'w':'i');
|
||
a.textContent=(r.archetype==='erratic'?'⚠ ':'📵 ')+r.cnt+' '+r.archetype+' workers';
|
||
el.appendChild(a);
|
||
});
|
||
var g=document.createElement('div');g.className='alrt g';g.textContent='✓ All systems online';
|
||
el.appendChild(g);
|
||
}).catch(function(){});
|
||
}
|
||
|
||
function renderPipeline(today) {
|
||
var el = document.getElementById('pipeline');
|
||
el.textContent = '';
|
||
var u=0,f=0,fl=0,t=0;
|
||
if (today&&today.contracts) today.contracts.forEach(function(c){
|
||
t++;if(c.priority==='urgent')u++;if(c.filled>=c.headcount)fl++;else f++;
|
||
});
|
||
[['red',u,'Urgent'],['yel',f,'Filling'],['blu',t,'Contracts'],['grn',fl,'Filled']].forEach(function(p){
|
||
var d=document.createElement('div');d.className='pip '+p[0];
|
||
var n=document.createElement('div');n.className='n';n.textContent=p[1];
|
||
var l=document.createElement('div');l.className='l';l.textContent=p[2];
|
||
d.appendChild(n);d.appendChild(l);el.appendChild(d);
|
||
});
|
||
}
|
||
|
||
function parseWorker(text) {
|
||
var parts = (text||'').split(/—|–/);
|
||
var name = parts[0] ? parts[0].trim() : '?';
|
||
var rest = parts[1] ? parts[1].trim() : '';
|
||
var rm = rest.match(/^(.+?) in (.+?)\./);
|
||
var sm = rest.match(/Skills: ([^.]+)/);
|
||
var cm = rest.match(/Certs?: ([^.]+)/);
|
||
var rr = rest.match(/Reliability: ([\d.]+)/);
|
||
var av = rest.match(/Availability: ([\d.]+)/);
|
||
var ar = rest.match(/Archetype: (\w+)/);
|
||
return {
|
||
name: name, role: rm?rm[1]:'', location: rm?rm[2]:'',
|
||
skills: sm?sm[1].split('|').slice(0,4):[],
|
||
certs: cm?cm[1].split('|').filter(function(c){return c!=='none'}):[],
|
||
reliability: rr?parseFloat(rr[1]):0,
|
||
availability: av?parseFloat(av[1]):0,
|
||
archetype: ar?ar[1]:''
|
||
};
|
||
}
|
||
|
||
function renderContracts(today) {
|
||
var el = document.getElementById('contracts');
|
||
el.textContent = '';
|
||
if (!today||!today.contracts||!today.contracts.length) { el.textContent='No contracts'; return; }
|
||
var sorted = today.contracts.slice().sort(function(a,b){
|
||
var p={urgent:0,high:1,medium:2,low:3};
|
||
if(a.filled>=a.headcount&&b.filled<b.headcount)return 1;
|
||
if(a.filled<a.headcount&&b.filled>=b.headcount)return -1;
|
||
return (p[a.priority]||2)-(p[b.priority]||2);
|
||
});
|
||
sorted.forEach(function(c) {
|
||
var isFilled = c.filled >= c.headcount;
|
||
var card = document.createElement('div'); card.className = 'contract';
|
||
|
||
// Header
|
||
var hdr = document.createElement('div'); hdr.className = 'hdr';
|
||
var left = document.createElement('div'); left.className = 'left';
|
||
var dot = document.createElement('div'); dot.className = 'urgency ' + (isFilled?'filled':c.priority);
|
||
var cl = document.createElement('span'); cl.className = 'client'; cl.textContent = c.client;
|
||
var need = document.createElement('span'); need.className = 'need';
|
||
need.textContent = c.role + ' × ' + c.headcount + ' · ' + (c.city||c.state) + ' · ' + c.start;
|
||
left.appendChild(dot); left.appendChild(cl); left.appendChild(need);
|
||
|
||
var right = document.createElement('div'); right.className = 'right';
|
||
var prog = document.createElement('div'); prog.className = 'progress';
|
||
var fill = document.createElement('div'); fill.className = 'fill ' + (isFilled?'full':c.filled>0?'partial':'empty');
|
||
fill.style.width = Math.round(c.filled/c.headcount*100) + '%';
|
||
prog.appendChild(fill);
|
||
var cnt = document.createElement('span'); cnt.className = 'count'; cnt.textContent = c.filled + '/' + c.headcount;
|
||
right.appendChild(prog); right.appendChild(cnt);
|
||
hdr.appendChild(left); hdr.appendChild(right);
|
||
card.appendChild(hdr);
|
||
|
||
// Workers
|
||
if (c.matches && c.matches.length) {
|
||
var wdiv = document.createElement('div'); wdiv.className = 'workers';
|
||
c.matches.slice(0, c.headcount).forEach(function(m, idx) {
|
||
var w = parseWorker(m.chunk_text || '');
|
||
var wcard = document.createElement('div'); wcard.className = 'worker';
|
||
|
||
// Avatar
|
||
var av = document.createElement('div');
|
||
av.className = 'avatar ' + colors[idx % colors.length];
|
||
av.textContent = (w.name||'?').split(' ').map(function(n){return n[0]||''}).join('').substring(0,2);
|
||
wcard.appendChild(av);
|
||
|
||
// Info
|
||
var info = document.createElement('div'); info.className = 'info';
|
||
var nameRow = document.createElement('div'); nameRow.className = 'name-row';
|
||
var nm = document.createElement('span'); nm.className = 'wname'; nm.textContent = w.name || m.name || m.doc_id;
|
||
var loc = document.createElement('span'); loc.className = 'wloc'; loc.textContent = w.location;
|
||
nameRow.appendChild(nm); nameRow.appendChild(loc);
|
||
info.appendChild(nameRow);
|
||
|
||
// Tags
|
||
var tags = document.createElement('div'); tags.className = 'tags';
|
||
w.skills.slice(0,3).forEach(function(s) {
|
||
var t = document.createElement('span'); t.className = 'tag skill'; t.textContent = s.trim(); tags.appendChild(t);
|
||
});
|
||
w.certs.slice(0,2).forEach(function(c) {
|
||
var t = document.createElement('span'); t.className = 'tag cert'; t.textContent = c.trim(); tags.appendChild(t);
|
||
});
|
||
if (w.archetype) {
|
||
var t = document.createElement('span'); t.className = 'tag type'; t.textContent = w.archetype; tags.appendChild(t);
|
||
}
|
||
info.appendChild(tags);
|
||
|
||
// Metrics
|
||
var metrics = document.createElement('div'); metrics.className = 'metrics';
|
||
addMetric(metrics, 'Reliability', w.reliability);
|
||
addMetric(metrics, 'Availability', w.availability);
|
||
info.appendChild(metrics);
|
||
wcard.appendChild(info);
|
||
|
||
// Actions
|
||
var actions = document.createElement('div'); actions.className = 'actions';
|
||
var call = document.createElement('button'); call.className = 'wbtn primary'; call.textContent = 'Call';
|
||
var sms = document.createElement('button'); sms.className = 'wbtn'; sms.textContent = 'SMS';
|
||
actions.appendChild(call); actions.appendChild(sms);
|
||
wcard.appendChild(actions);
|
||
|
||
wdiv.appendChild(wcard);
|
||
});
|
||
card.appendChild(wdiv);
|
||
}
|
||
el.appendChild(card);
|
||
});
|
||
}
|
||
|
||
function addMetric(parent, label, val) {
|
||
var m = document.createElement('div'); m.className = 'metric';
|
||
var l = document.createElement('span'); l.className = 'label'; l.textContent = label;
|
||
var bar = document.createElement('div'); bar.className = 'bar';
|
||
var v = document.createElement('div');
|
||
v.className = 'val ' + (val >= 0.8 ? 'high' : val >= 0.5 ? 'med' : 'low');
|
||
v.style.width = Math.round(val * 100) + '%';
|
||
bar.appendChild(v);
|
||
var pct = document.createElement('span'); pct.className = 'pct';
|
||
pct.style.color = val >= 0.8 ? '#3fb950' : val >= 0.5 ? '#d29922' : '#f85149';
|
||
pct.textContent = Math.round(val * 100) + '%';
|
||
m.appendChild(l); m.appendChild(bar); m.appendChild(pct);
|
||
parent.appendChild(m);
|
||
}
|
||
|
||
function doSearch() {
|
||
var q=document.getElementById('sq').value.trim();if(!q)return;
|
||
var st=document.getElementById('sst').value;
|
||
var rl=document.getElementById('srl').value;
|
||
var out=document.getElementById('sresults');
|
||
out.textContent=''; var ld=document.createElement('div');ld.className='loading';ld.textContent='Searching...';out.appendChild(ld);
|
||
var f="CAST(reliability AS DOUBLE)>=0.5";
|
||
if(st)f+=" AND state='"+st+"'";if(rl)f+=" AND role='"+rl+"'";
|
||
fetch(A+'/search',{method:'POST',headers:{'Content-Type':'application/json'},
|
||
body:JSON.stringify({question:q,index_name:'workers_500k_v1',sql_filter:f,dataset:'workers_500k',id_column:'worker_id',top_k:8,generate:false})
|
||
}).then(function(r){return r.json()}).then(function(d){
|
||
out.textContent='';
|
||
var sources=d.sources||[];if(!sources.length){out.textContent='No matches.';return}
|
||
var h=document.createElement('div');h.style.cssText='color:#8b949e;font-size:11px;margin-bottom:8px';
|
||
h.textContent=(d.sql_matches||0)+' matches → '+sources.length+' best ('+(d.duration_ms||0)+'ms)';out.appendChild(h);
|
||
sources.forEach(function(s,idx){
|
||
var w=parseWorker(s.chunk_text);
|
||
var wcard=document.createElement('div');wcard.className='worker';wcard.style.cssText='background:#161b22;border:1px solid #21262d;border-radius:8px;padding:10px;margin-bottom:6px';
|
||
var av=document.createElement('div');av.className='avatar '+colors[idx%colors.length];
|
||
av.textContent=(w.name||'?').split(' ').map(function(n){return n[0]||''}).join('').substring(0,2);wcard.appendChild(av);
|
||
var info=document.createElement('div');info.className='info';
|
||
var nr=document.createElement('div');nr.className='name-row';
|
||
var nm=document.createElement('span');nm.className='wname';nm.textContent=w.name;
|
||
var loc=document.createElement('span');loc.className='wloc';loc.textContent=w.location;
|
||
nr.appendChild(nm);nr.appendChild(loc);info.appendChild(nr);
|
||
var tags=document.createElement('div');tags.className='tags';
|
||
w.skills.slice(0,3).forEach(function(sk){var t=document.createElement('span');t.className='tag skill';t.textContent=sk.trim();tags.appendChild(t)});
|
||
w.certs.slice(0,2).forEach(function(c){var t=document.createElement('span');t.className='tag cert';t.textContent=c.trim();tags.appendChild(t)});
|
||
info.appendChild(tags);
|
||
var metrics=document.createElement('div');metrics.className='metrics';
|
||
addMetric(metrics,'Rel',w.reliability);addMetric(metrics,'Avail',w.availability);
|
||
info.appendChild(metrics);wcard.appendChild(info);out.appendChild(wcard);
|
||
});
|
||
}).catch(function(e){out.textContent='Error: '+e.message});
|
||
}
|
||
</script>
|
||
</body></html>
|