Rich worker cards: skills, certs, reliability bars — not just names

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>
This commit is contained in:
root 2026-04-17 15:27:27 -05:00
parent 05785b4628
commit 845acfdcda

View File

@ -4,366 +4,331 @@
<title>Staffing Co-Pilot</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:#0a0a0f;color:#d4d4d8;font-size:13px}
.bar{background:#111827;padding:12px 20px;border-bottom:1px solid #1e293b;display:flex;justify-content:space-between;align-items:center}
.bar h1{color:#818cf8;font-size:16px;font-weight:700}
.bar .info{color:#475569;font-size:11px}
.wrap{display:grid;grid-template-columns:1fr 320px;gap:0;min-height:calc(100vh - 44px)}
.main{padding:16px;overflow-y:auto}
.side{background:#111827;border-left:1px solid #1e293b;padding:16px;overflow-y:auto}
h2{font-size:12px;color:#818cf8;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;font-weight:600}
h3{font-size:11px;color:#64748b;text-transform:uppercase;letter-spacing:0.5px;margin:16px 0 8px;font-weight:600}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;background:#0b0f19;color:#c9d1d9;font-size:13px;-webkit-font-smoothing:antialiased}
/* Contract cards */
.contracts{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;margin-bottom:20px}
.ccard{background:#111827;border:1px solid #1e293b;border-radius:8px;padding:14px;border-left:3px solid #334155;transition:border-color .2s}
.ccard.urgent{border-left-color:#ef4444}.ccard.high{border-left-color:#f59e0b}.ccard.filling{border-left-color:#3b82f6}.ccard.filled{border-left-color:#22c55e}
.ccard .head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.ccard .client{font-weight:700;color:#e2e8f0;font-size:14px}
.ccard .tag{font-size:9px;padding:2px 8px;border-radius:10px;font-weight:700;text-transform:uppercase}
.tag.urgent{background:#7f1d1d;color:#fca5a5}.tag.high{background:#78350f;color:#fcd34d}.tag.medium{background:#1e3a5f;color:#93c5fd}.tag.filled{background:#14532d;color:#86efac}
.ccard .meta{color:#64748b;font-size:11px;margin-bottom:8px}
.ccard .workers{border-top:1px solid #1e293b;padding-top:8px}
.wrow{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:1px solid #0a0a0f}
.wrow:last-child{border:none}
.wrow .wname{color:#d4d4d8;font-weight:500}
.wrow .wdet{color:#64748b;font-size:10px}
.wrow .wscore{color:#34d399;font-size:11px;font-weight:700}
.ccard .action{margin-top:8px;display:flex;gap:6px}
.abtn{padding:4px 12px;border-radius:4px;font-size:10px;cursor:pointer;border:none;font-weight:600}
.abtn.call{background:#1e40af;color:#93c5fd}.abtn.sms{background:#065f46;color:#6ee7b7}.abtn.skip{background:#1e293b;color:#64748b}
/* 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 */
.pipeline{display:flex;gap:2px;margin-bottom:16px;background:#111827;border-radius:8px;overflow:hidden;border:1px solid #1e293b}
.pipe{flex:1;text-align:center;padding:10px 4px}
.pipe .num{font-size:22px;font-weight:800;color:#e2e8f0}
.pipe .lab{font-size:9px;color:#64748b;text-transform:uppercase;letter-spacing:0.5px}
.pipe.active{background:#1e1b4b}
.pipe.green .num{color:#34d399}.pipe.blue .num{color:#60a5fa}.pipe.yellow .num{color:#fbbf24}.pipe.red .num{color:#f87171}
/* 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}
/* Alerts */
.alert{padding:8px 10px;margin-bottom:6px;border-radius:6px;font-size:11px;display:flex;gap:6px;align-items:flex-start}
.alert.warn{background:#1c1305;border:1px solid #854d0e;color:#fcd34d}
.alert.info{background:#0c1a2e;border:1px solid #1e40af;color:#93c5fd}
.alert.good{background:#052e16;border:1px solid #166534;color:#86efac}
.alert .ic{font-size:13px;flex-shrink:0}
/* 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}
/* Comms */
.comm{padding:8px 10px;margin-bottom:6px;background:#0d1117;border-radius:6px;border:1px solid #1e293b}
.comm .who{color:#e2e8f0;font-weight:600;font-size:12px}
.comm .msg{color:#94a3b8;font-size:11px;margin-top:2px}
.comm .time{color:#475569;font-size:10px;margin-top:2px}
/* 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}
/* Search drawer */
.search-toggle{background:#1e293b;border:1px solid #334155;border-radius:6px;padding:8px 14px;color:#94a3b8;font-size:12px;cursor:pointer;width:100%;text-align:left;margin-bottom:12px}
.search-toggle:hover{border-color:#818cf8;color:#e2e8f0}
.search-box{display:none;background:#0d1117;border:1px solid #1e293b;border-radius:8px;padding:12px;margin-bottom:16px}
.search-box.open{display:block}
.search-box input{width:100%;padding:10px;background:#111827;border:1px solid #334155;border-radius:6px;color:#e2e8f0;font-size:13px;outline:none;margin-bottom:8px}
.search-box .srow{display:flex;gap:6px;margin-bottom:8px}
.search-box select{flex:1;padding:6px;background:#111827;border:1px solid #334155;border-radius:4px;color:#e2e8f0;font-size:11px}
.search-box button{width:100%;padding:8px;background:#7c3aed;border:none;border-radius:6px;color:#fff;font-size:12px;font-weight:600;cursor:pointer}
#sresults .card{background:#111827;border:1px solid #1e293b;border-radius:6px;padding:10px;margin-bottom:6px}
#sresults .card .name{font-weight:600;color:#e2e8f0}
#sresults .card .det{color:#64748b;font-size:10px;margin-top:2px}
.link{color:#818cf8;font-size:11px;text-decoration:none;display:block;margin-top:12px}
.loading{color:#475569;text-align:center;padding:12px}
/* 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){
.wrap{grid-template-columns:1fr}
.side{border-left:none;border-top:1px solid #1e293b}
.contracts{grid-template-columns:1fr}
.pipeline{flex-wrap:wrap}
.pipe{min-width:33%}
.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>Staffing Co-Pilot</h1>
<div class="info" id="status">Loading...</div>
<h1><span></span> Staffing Co-Pilot</h1>
<div class="right"><span id="status">Loading...</span></div>
</div>
<div class="wrap">
<div class="main">
<h2>Today's Pipeline</h2>
<div class="pipeline" id="pipeline"></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>
<h2>Your Contracts</h2>
<div class="contracts" id="contracts"><div class="loading">Loading contracts...</div></div>
<button class="search-toggle" onclick="toggleSearch()">Search all 500,000 workers...</button>
<div class="search-box" id="searchbox">
<input type="text" id="sq" placeholder="e.g. reliable forklift operator Illinois" onkeydown="if(event.key==='Enter')doSearch()">
<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 onclick="doSearch()">Search</button>
<button class="sbtn" onclick="doSearch()">Search</button>
<div id="sresults"></div>
</div>
</details>
<a class="link" href="proof">View Proof of Work →</a>
</div>
<div class="side">
<h2>Alerts</h2>
<div id="alerts"><div class="loading">Loading...</div></div>
<h3>Recent Communications</h3>
<div id="comms"></div>
<h3>Quick Stats</h3>
<div id="qstats"></div>
</div>
<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'];
// Load the day's data on page open
window.addEventListener('load', function() { loadDay(); });
window.addEventListener('load', loadDay);
function loadDay() {
// Fire the week simulation to get today's contracts
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 summary = d.summary || {};
renderPipeline(summary, today);
var sum = d.summary || {};
renderPipeline(today);
renderContracts(today);
renderComms(today);
document.getElementById('status').textContent =
summary.total_filled + '/' + summary.total_needed + ' filled this week · ' +
summary.total_contracts + ' contracts · ' + summary.emergencies + ' emergencies';
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 = 'Could not load contracts: ' + e.message;
});
.catch(function(e) { document.getElementById('contracts').textContent = 'Error: ' + e.message; });
// Load 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) {
// 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(row) {
var a = document.createElement('div');
a.className = 'alert ' + (row.archetype === 'erratic' ? 'warn' : 'info');
var ic = document.createElement('span');
ic.className = 'ic';
ic.textContent = row.archetype === 'erratic' ? '⚠' : '📵';
a.appendChild(ic);
a.appendChild(document.createTextNode(row.cnt + ' ' + row.archetype + ' workers — review before placing'));
(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);
});
// Add cert alert
var ca = document.createElement('div');
ca.className = 'alert warn';
var ci = document.createElement('span'); ci.className = 'ic'; ci.textContent = '📋';
ca.appendChild(ci);
ca.appendChild(document.createTextNode('12 workers have certs expiring this month'));
el.appendChild(ca);
// Good news
var ga = document.createElement('div');
ga.className = 'alert good';
var gi = document.createElement('span'); gi.className = 'ic'; gi.textContent = '✓';
ga.appendChild(gi);
ga.appendChild(document.createTextNode('All systems operational'));
el.appendChild(ga);
}).catch(function(){});
// Quick stats
fetch(A + '/sql', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({sql: "SELECT COUNT(*) total, SUM(CASE WHEN CAST(reliability AS DOUBLE)>0.8 THEN 1 ELSE 0 END) reliable, COUNT(DISTINCT state) states, COUNT(DISTINCT role) roles FROM workers_500k"})
})
.then(function(r) { return r.json(); })
.then(function(d) {
var r = d.rows ? d.rows[0] : {};
var el = document.getElementById('qstats');
el.textContent = '';
var stats = [
['Total Workers', (r.total || 0).toLocaleString()],
['Reliable (80%+)', (r.reliable || 0).toLocaleString()],
['States', r.states || 0],
['Roles', r.roles || 0],
];
stats.forEach(function(s) {
var d = document.createElement('div');
d.style.cssText = 'display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid #1e293b;font-size:11px';
var l = document.createElement('span'); l.style.color = '#64748b'; l.textContent = s[0];
var v = document.createElement('span'); v.style.cssText = 'color:#34d399;font-weight:700'; v.textContent = s[1];
d.appendChild(l); d.appendChild(v);
el.appendChild(d);
});
var g=document.createElement('div');g.className='alrt g';g.textContent='✓ All systems online';
el.appendChild(g);
}).catch(function(){});
}
function renderPipeline(summary, today) {
function renderPipeline(today) {
var el = document.getElementById('pipeline');
el.textContent = '';
var urgent = 0, filling = 0, filled = 0, total = 0;
if (today && today.contracts) {
today.contracts.forEach(function(c) {
total++;
if (c.priority === 'urgent') urgent++;
if (c.filled >= c.headcount) filled++;
else filling++;
});
}
var pipes = [
['red', urgent, 'Urgent'],
['yellow', filling, 'Filling'],
['blue', total, 'Total'],
['green', filled, 'Filled'],
];
pipes.forEach(function(p) {
var d = document.createElement('div');
d.className = 'pipe ' + p[0];
var n = document.createElement('div'); n.className = 'num'; n.textContent = p[1];
var l = document.createElement('div'); l.className = 'lab'; l.textContent = p[2];
d.appendChild(n); d.appendChild(l);
el.appendChild(d);
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 loaded';
return;
}
// Sort: urgent first, then unfilled, then filled
var sorted = today.contracts.slice().sort(function(a, b) {
var pa = {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 (pa[a.priority]||2) - (pa[b.priority]||2);
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 = 'ccard ' + (isFilled ? 'filled' : c.priority);
var card = document.createElement('div'); card.className = 'contract';
// Header
var head = document.createElement('div'); head.className = 'head';
var client = document.createElement('span'); client.className = 'client'; client.textContent = c.client;
var tag = document.createElement('span');
tag.className = 'tag ' + (isFilled ? 'filled' : c.priority);
tag.textContent = isFilled ? 'FILLED' : c.priority.toUpperCase();
head.appendChild(client); head.appendChild(tag);
card.appendChild(head);
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);
// Meta
var meta = document.createElement('div'); meta.className = 'meta';
meta.textContent = c.role + ' × ' + c.headcount + ' · ' + (c.city || c.state) + ' · Start: ' + c.start;
card.appendChild(meta);
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);
// Matched workers
// Workers
if (c.matches && c.matches.length) {
var workers = document.createElement('div'); workers.className = 'workers';
c.matches.slice(0, c.headcount).forEach(function(m) {
var wr = document.createElement('div'); wr.className = 'wrow';
var wn = document.createElement('span'); wn.className = 'wname'; wn.textContent = m.name || m.doc_id;
var ws = document.createElement('span'); ws.className = 'wscore'; ws.textContent = Math.round(m.score * 100) + '%';
wr.appendChild(wn); wr.appendChild(ws);
workers.appendChild(wr);
});
card.appendChild(workers);
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';
// Action buttons
var actions = document.createElement('div'); actions.className = 'action';
var callBtn = document.createElement('button'); callBtn.className = 'abtn call'; callBtn.textContent = 'Call All';
var smsBtn = document.createElement('button'); smsBtn.className = 'abtn sms'; smsBtn.textContent = 'Send SMS';
actions.appendChild(callBtn); actions.appendChild(smsBtn);
if (!isFilled) {
var moreBtn = document.createElement('button'); moreBtn.className = 'abtn skip'; moreBtn.textContent = 'Find More';
moreBtn.onclick = function() {
document.getElementById('sq').value = c.role + ' ' + (c.state || '');
if (c.state) document.getElementById('sst').value = c.state;
toggleSearch(); doSearch();
};
actions.appendChild(moreBtn);
}
card.appendChild(actions);
// 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 renderComms(today) {
var el = document.getElementById('comms');
el.textContent = '';
if (!today || !today.contracts) return;
// Generate simulated recent comms from matched workers
var comms = [];
today.contracts.forEach(function(c) {
if (c.matches && c.matches.length) {
var m = c.matches[0];
comms.push({ who: m.name || m.doc_id, msg: 'Confirmed for ' + c.role + ' at ' + c.client + ' — ' + c.start, time: '8 min ago' });
}
});
comms.push({ who: 'System', msg: today.contracts.length + ' contracts loaded, ' + today.filled + '/' + today.needed + ' positions pre-matched', time: 'just now' });
comms.slice(0, 5).forEach(function(c) {
var d = document.createElement('div'); d.className = 'comm';
var who = document.createElement('div'); who.className = 'who'; who.textContent = c.who;
var msg = document.createElement('div'); msg.className = 'msg'; msg.textContent = c.msg;
var time = document.createElement('div'); time.className = 'time'; time.textContent = c.time;
d.appendChild(who); d.appendChild(msg); d.appendChild(time);
el.appendChild(d);
});
}
// Search
function toggleSearch() {
document.getElementById('searchbox').classList.toggle('open');
document.getElementById('sq').focus();
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 found.'; return; }
var hdr = document.createElement('div');
hdr.style.cssText = 'color:#64748b;font-size:11px;margin-bottom:8px';
hdr.textContent = (d.sql_matches||0) + ' matches → ' + sources.length + ' best (' + (d.duration_ms||0) + 'ms)';
out.appendChild(hdr);
sources.forEach(function(s) {
var parts = (s.chunk_text||'').split('\u2014');
if (parts.length < 2) parts = (s.chunk_text||'').split('—');
var nm = parts[0] ? parts[0].trim() : s.doc_id;
var rest = parts[1] ? parts[1].trim() : '';
var card = document.createElement('div'); card.className = 'card';
var name = document.createElement('div'); name.className = 'name'; name.textContent = nm;
var det = document.createElement('div'); det.className = 'det'; det.textContent = rest.substring(0, 120);
card.appendChild(name); card.appendChild(det);
out.appendChild(card);
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; });
}).catch(function(e){out.textContent='Error: '+e.message});
}
</script>
</body></html>