Urgent pipeline: step-by-step workflow walks staffer through emergency fills

Urgent contracts now show a 4-step action plan:
  Step 1 (red): Review pre-matched workers
  Step 2 (yellow): Call first choice — highest match score
  Step 3 (blue): Confirm or replace — backup is ready
  Step 4 (green): Send shift details to confirmed workers

First-choice worker highlighted with red border + label.
Backup workers shown with dimmed styling + 'BACKUP' label.
Urgent cards show ALL matched workers + backups (not just 3).

Non-urgent contracts split into 'In Progress' (still filling)
and 'Ready to Go' (fully staffed) sections.

The staffer doesn't stare at a red label wondering what to do.
They follow the steps: review, call, confirm, send. Done.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
root 2026-04-17 16:08:18 -05:00
parent c0ff7434cb
commit 45a95a9feb

View File

@ -102,27 +102,58 @@ function renderMain(today,sum,roles,topWorkers,coverage){
addStat(stats,'500K','Workers in System');
el.appendChild(stats);
// INSIGHT 1: Contracts that need attention
// INSIGHT 1: Urgent pipeline — step-by-step workflow
if(today&&today.contracts){
var urgent=today.contracts.filter(function(c){return c.priority==='urgent'||c.filled<c.headcount});
var urgent=today.contracts.filter(function(c){return c.priority==='urgent'});
var needsWork=today.contracts.filter(function(c){return c.priority!=='urgent'&&c.filled<c.headcount});
var filled=today.contracts.filter(function(c){return c.filled>=c.headcount});
if(urgent.length){
var ins=makeInsight('urgent','Needs Your Attention',
urgent.length+' contract'+(urgent.length>1?'s':'')+' need'+(urgent.length===1?'s':'')+' action — workers are pre-matched and ready',
'The system analyzed '+sum.total_needed+' positions across all contracts and found the best available matches. These still need your confirmation.');
var ins=makeInsight('urgent','Urgent Pipeline',
urgent.length+' emergency contract'+(urgent.length>1?'s':'') +' — the system found workers, here\'s your action plan',
null);
// Pipeline steps explanation
var steps=document.createElement('div');
steps.style.cssText='display:flex;gap:4px;margin-bottom:16px;flex-wrap:wrap';
var stepData=[
['1','Review','Check the pre-matched workers below','#f85149'],
['2','Call first choice','Highest-rated worker gets the first call','#d29922'],
['3','Confirm or replace','If they can\'t make it, the backup is ready','#58a6ff'],
['4','Send details','Client address, start time, dress code','#3fb950']
];
stepData.forEach(function(s){
var sd=document.createElement('div');
sd.style.cssText='flex:1;min-width:120px;background:#0d1117;border-radius:8px;padding:10px;border-top:2px solid '+s[3];
var num=document.createElement('div');num.style.cssText='font-size:18px;font-weight:800;color:'+s[3];num.textContent='Step '+s[0];
var title=document.createElement('div');title.style.cssText='font-size:12px;font-weight:600;color:#f0f6fc;margin-top:2px';title.textContent=s[1];
var desc=document.createElement('div');desc.style.cssText='font-size:10px;color:#8b949e;margin-top:2px';desc.textContent=s[2];
sd.appendChild(num);sd.appendChild(title);sd.appendChild(desc);steps.appendChild(sd);
});
ins.appendChild(steps);
urgent.forEach(function(c){
addContractInsight(ins,c);
addContractInsight(ins,c,true);
});
el.appendChild(ins);
}
// Non-urgent that need work
if(needsWork.length){
var ins1b=makeInsight('warning','In Progress',
needsWork.length+' contract'+(needsWork.length>1?'s':'')+' still filling — workers matched, awaiting confirmation',
'These are standard priority. Workers are ranked by fit — start from the top.');
needsWork.forEach(function(c){
addContractInsight(ins1b,c,false);
});
el.appendChild(ins1b);
}
if(filled.length){
var ins2=makeInsight('opportunity','Ready to Confirm',
filled.length+' contract'+(filled.length>1?'s':'')+' fully matched — review and send outreach',
'These contracts have enough qualified workers matched. The system ranked them by skills, reliability, and location fit.');
var ins2=makeInsight('opportunity','Ready to Go',
filled.length+' contract'+(filled.length>1?'s':'')+' fully staffed — review and send shift details',
'All positions matched. The workers listed below are the system\'s best picks based on role, location, reliability, and certifications. Tap Call or SMS to confirm.');
filled.forEach(function(c){
addContractInsight(ins2,c);
addContractInsight(ins2,c,false);
});
el.appendChild(ins2);
}
@ -170,7 +201,7 @@ function makeInsight(type,headline,sub,explanation){
return d;
}
function addContractInsight(parent,c){
function addContractInsight(parent,c,isUrgent){
var isFilled=c.filled>=c.headcount;
var cd=document.createElement('div');cd.style.cssText='background:#0d1117;border-radius:8px;padding:12px;margin-bottom:8px';
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;align-items:center;margin-bottom:8px';
@ -184,15 +215,22 @@ function addContractInsight(parent,c){
hdr.appendChild(left);hdr.appendChild(right);cd.appendChild(hdr);
if(c.matches&&c.matches.length){
c.matches.slice(0,Math.min(c.headcount,3)).forEach(function(m,i){
var showCount=Math.min(c.headcount,isUrgent?c.headcount+2:3);
c.matches.slice(0,showCount).forEach(function(m,i){
var w=pw(m.chunk_text||'');if(!w.nm)w.nm=m.name||m.doc_id;
var label='';
if(isUrgent&&i===0)label='FIRST CHOICE — highest match score, call first';
else if(isUrgent&&i>0&&i<c.headcount)label='';
else if(isUrgent&&i>=c.headcount)label='BACKUP — if someone above can\'t make it';
addWorkerInsight(cd,w.nm,
[w.role,w.loc].filter(Boolean).join(' · '),
buildWhyText(w,c),i);
label||buildWhyText(w,c),i,
isUrgent&&i===0?'#f85149':isUrgent&&i>=c.headcount?'#484f58':null);
});
if(c.matches.length>3){
var remaining=c.matches.length-showCount;
if(remaining>0){
var more=document.createElement('div');more.style.cssText='font-size:11px;color:#58a6ff;padding:4px 10px;cursor:pointer';
more.textContent='+ '+(c.matches.length-3)+' more matched workers';
more.textContent='+ '+remaining+' more available workers';
cd.appendChild(more);
}
}
@ -215,8 +253,9 @@ function buildWhyText(w,c){
return reasons.length?reasons.join(' · '):'Matched by AI based on role and skills';
}
function addWorkerInsight(parent,name,detail,why,idx){
function addWorkerInsight(parent,name,detail,why,idx,highlight){
var w=document.createElement('div');w.className='iworker';
if(highlight)w.style.borderLeft='3px solid '+highlight;
var av=document.createElement('div');av.className='av';av.style.background=AC[(idx||0)%AC.length];
av.textContent=(name||'?').split(' ').map(function(n){return(n[0]||'').toUpperCase()}).join('').substring(0,2);
w.appendChild(av);