← back to Ticket System
office: seat working agents in their doing room (rank active work over latest touch) + persistent working glow
ee1e135b05127fd0c0d70fb9282203aff70c19f7 · 2026-07-27 09:24:54 -0700 · Steve Abrams
Files touched
Diff
commit ee1e135b05127fd0c0d70fb9282203aff70c19f7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 27 09:24:54 2026 -0700
office: seat working agents in their doing room (rank active work over latest touch) + persistent working glow
---
office.html | 38 ++++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/office.html b/office.html
index aa1314e..65fce39 100644
--- a/office.html
+++ b/office.html
@@ -337,11 +337,27 @@ let seenMsgMids=null, lastActionTs=0, lastFetch=0;
let showLabels=true, showDone=true, followDMs=false;
let laneSigns=null;
-function agentLatestRoom(tk){ const at=new Map();
- const put=(a,ts,id)=>{ if(!a)return; const c=at.get(a); if(!c||c.ts<ts) at.set(a,{ts,ticketId:id}); };
- for(const t of tk){ (t.actions||[]).forEach(a=>put(a.agent,a.ts,t.id));
- (t.comments||[]).forEach(c=>put(c.agent,c.ts,t.id)); if(t.assignee) put(t.assignee,t.updated_at,t.id); }
- return at; }
+// Where each agent stands. An agent that is WORKING — owns or is acting on a
+// live doing/blocked ticket — belongs in THAT room, even if they more-recently
+// dropped a stray comment on some done ticket. So we rank active lanes (and being
+// the assignee) above pure recency, and only fall back to latest-touch for agents
+// with no active work. Returns name → { ticketId, working }.
+function agentPlacement(tk){
+ const ACTIVE={ doing:3, blocked:2, open:1, done:0 };
+ const best=new Map();
+ const put=(name,ts,t,assigned)=>{ if(!name) return;
+ // rank: active lane dominates; assignee of that ticket beats a mere toucher
+ const rank=(ACTIVE[t.status]??0)*10 + (assigned?2:0);
+ const cur=best.get(name);
+ if(!cur || rank>cur.rank || (rank===cur.rank && ts>cur.ts))
+ best.set(name,{ ts, ticketId:t.id, rank, working:t.status==='doing' });
+ };
+ for(const t of tk){
+ (t.actions ||[]).forEach(a=>put(a.agent,+new Date(a.ts),t, t.assignee===a.agent));
+ (t.comments||[]).forEach(c=>put(c.agent,+new Date(c.ts),t, t.assignee===c.agent));
+ if(t.assignee) put(t.assignee,+new Date(t.updated_at),t,true);
+ }
+ return best; }
function layout(tk){
const byLane={doing:[],blocked:[],open:[],done:[]};
@@ -383,17 +399,18 @@ function updateRoom(room,t){ const u=room.userData, prev=u.ticket; u.ticket=t;
}
function slotInRoom(p,i,n){ if(n<=1) return new THREE.Vector3(p.x,0,p.z+1.2);
const r=Math.min(2.6,1.2+n*0.15),a=(i/n)*Math.PI*2; return new THREE.Vector3(p.x+Math.cos(a)*r,0,p.z+Math.sin(a)*r); }
-function placeAvatars(tk){ const latest=agentLatestRoom(tk), perRoom=new Map(), assign=[];
- for(const [name,info] of latest){ const room=roomByTicket.get(info.ticketId);
- const rid=(room&&room.visible)?info.ticketId:null; assign.push({name,rid});
+function placeAvatars(tk){ const place=agentPlacement(tk), perRoom=new Map(), assign=[];
+ for(const [name,info] of place){ const room=roomByTicket.get(info.ticketId);
+ const rid=(room&&room.visible)?info.ticketId:null; assign.push({name,rid,working:info.working&&!!rid});
if(rid) perRoom.set(rid,(perRoom.get(rid)||0)+1); }
const lobby=[], idx=new Map();
for(const a of assign){ let av=avatarByName.get(a.name);
if(!av){ av=buildAvatar(a.name); avatarByName.set(a.name,av); avatarsGrp.add(av);
av.position.set(a.rid?roomByTicket.get(a.rid).position.x:-46, 0, a.rid?roomByTicket.get(a.rid).position.z:30); }
+ av.userData.atWork=a.working; // persistent "working" glow while in a doing room
if(a.rid){ const room=roomByTicket.get(a.rid),n=perRoom.get(a.rid),i=idx.get(a.rid)||0; idx.set(a.rid,i+1);
av.userData.target.copy(slotInRoom(room.position,i,n)); av.userData.room=a.rid; } else lobby.push(av); }
- lobby.forEach((av,i)=>{ av.userData.target.set(-48+(i%10)*7,0,26+Math.floor(i/10)*7); av.userData.room=null; });
+ lobby.forEach((av,i)=>{ av.userData.atWork=false; av.userData.target.set(-48+(i%10)*7,0,26+Math.floor(i/10)*7); av.userData.room=null; });
}
async function poll(){
@@ -489,8 +506,9 @@ function tick(){
u.body.position.y=1.7+bob; u.head.position.y=3.3+bob;
if(moving){ const d=u.target.clone().sub(av.position); av.rotation.y=Math.atan2(d.x,d.z); }
if(u.working>0) u.working-=dt;
- const w=u.working>0; u.ring.material.opacity=w?(0.5+0.4*Math.sin(el*10)):0.18;
+ const w=u.working>0||u.atWork; u.ring.material.opacity=w?(0.5+0.4*Math.sin(el*10)):0.18;
u.ring.scale.setScalar(1+(w?0.18*Math.sin(el*10):0)); u.light.intensity=w?2.0+Math.sin(el*12):0.0;
+ u.body.material.emissiveIntensity=0.35+(w?0.28+0.22*Math.sin(el*6+u.phase):0); // working breath
u.label.visible=showLabels; }
// rooms
← 9e47637 canvas-design: LUMEN INDEX plate — Fleet Office as a scienti
·
back to Ticket System
·
office: harden agent placement — clear stale atWork glow on 338a454 →