441 lines
26 KiB
HTML
441 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>WEVAL Good Job!</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@600;800;900&display=swap');
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{background:#e8f0f8;overflow:hidden;font-family:'Nunito',sans-serif}
|
|
canvas{display:block}
|
|
#tip{position:fixed;pointer-events:none;display:none;z-index:99;background:#fff;border:3px solid;border-radius:16px;padding:12px 16px;color:#2a2a4a;box-shadow:0 6px 24px #00000018;max-width:230px}
|
|
#tip .tn{font-weight:900;font-size:1rem;color:#2a2a4a}
|
|
#tip .tt{font-size:.6rem;text-transform:uppercase;letter-spacing:2px;margin:2px 0 5px}
|
|
#tip .td{font-size:.78rem;color:#6a7a9a;line-height:1.3}
|
|
#tip .tp{font-size:.72rem;color:#e94560;font-weight:700;margin-top:4px}
|
|
#tip .st{font-size:.68rem;margin-top:3px;font-weight:800}
|
|
#hud{position:fixed;top:0;left:0;right:0;padding:8px 20px;display:flex;justify-content:space-between;align-items:center;z-index:10;background:#ffffffe0;backdrop-filter:blur(8px);border-bottom:2px solid #e0e8f0}
|
|
.logo{font-size:1.2rem;font-weight:900;color:#e94560}.logo b{color:#2a2a4a}
|
|
.hr{display:flex;gap:16px;font-size:.75rem;color:#6a7a9a;font-weight:700}
|
|
.hr b{padding:2px 8px;border-radius:8px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<div id="tip"><div class="tn"></div><div class="tt"></div><div class="td"></div><div class="tp"></div><div class="st"></div></div>
|
|
<div id="hud">
|
|
<div class="logo">WEVAL <b>Enterprise</b> ✨</div>
|
|
<div class="hr">
|
|
<span>👥 <b style="background:#dbeafe;color:#3b82f6" id="tot">31</b></span>
|
|
<span>🟢 <b style="background:#dcfce7;color:#16a34a" id="ac">0</b> actifs</span>
|
|
<span>📦 <b style="background:#fef3c7;color:#d97706" id="tc">0</b> tasks</span>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
const C=document.getElementById('c'),X=C.getContext('2d');
|
|
let W,H,mx=-1,my=-1,hov=null,fr=0,tasks=0;
|
|
function resize(){W=innerWidth;H=innerHeight;C.width=W*2;C.height=H*2;X.scale(2,2);doLayout()}
|
|
addEventListener('resize',resize);
|
|
|
|
// ═══ COLORS (bright pastel) ═══
|
|
const BG='#e8f0f8';
|
|
const FLOOR={
|
|
ceo:'#ffe0e6',sales:'#dbeafe',consult:'#e0d4fc',dev:'#d1fae5',
|
|
srv:'#fef3c7',sec:'#fce4ec',qa:'#cffafe',pharma:'#f3e8ff',ops:'#fefce8'
|
|
};
|
|
const WALL={
|
|
ceo:'#e94560',sales:'#3b82f6',consult:'#7c3aed',dev:'#10b981',
|
|
srv:'#f59e0b',sec:'#ef4444',qa:'#06b6d4',pharma:'#a855f7',ops:'#eab308'
|
|
};
|
|
|
|
// ═══ ROOMS ═══
|
|
const RM=[
|
|
{id:'ceo',label:'CEO',w:1.5,h:1.8},
|
|
{id:'sales',label:'Prospection',w:2.5,h:1.8},
|
|
{id:'consult',label:'Consulting',w:3,h:1.8},
|
|
{id:'dev',label:'Dev Lab',w:3.5,h:2.2},
|
|
{id:'srv',label:'Servers',w:2,h:2.2},
|
|
{id:'sec',label:'Sécurité',w:2,h:2.2},
|
|
{id:'qa',label:'QA Center',w:3,h:1.8},
|
|
{id:'pharma',label:'Pharma',w:2.5,h:1.8},
|
|
{id:'ops',label:'Monitoring',w:2.5,h:1.8},
|
|
];
|
|
RM.forEach(r=>{r.sx=0;r.sy=0;r.pw=0;r.ph=0;});
|
|
|
|
// ═══ CHAIN ═══
|
|
const CH=[
|
|
{l:'LEADS',c:'#3b82f6'},{l:'QUALIFY',c:'#7c3aed'},{l:'DESIGN',c:'#10b981'},
|
|
{l:'BUILD',c:'#22c55e'},{l:'SECURE',c:'#ef4444'},{l:'TEST',c:'#06b6d4'},
|
|
{l:'DEPLOY',c:'#f59e0b'},{l:'SHIP',c:'#e94560'},
|
|
];
|
|
CH.forEach(s=>{s.x=0;s.y=0;});
|
|
|
|
// ═══ AGENTS ═══
|
|
const AG=[
|
|
{n:'CEO',rm:'ceo',st:1,d:'Direction stratégique',p:'Décisions, budget',clr:'#e94560',hair:'#1a1a1a',skin:'#f5dcc0',htype:'slick'},
|
|
{n:'Ethica',rm:'sales',st:0,d:'Scraping HCP',p:'131K+ médecins',clr:'#3b82f6',hair:'#2a1200',skin:'#c9956a',htype:'curly'},
|
|
{n:'Analyst',rm:'sales',st:0,d:'Analyse besoins',p:'Specs marché',clr:'#3b82f6',hair:'#4a3020',skin:'#f5dcc0',htype:'short',glasses:1},
|
|
{n:'Writer',rm:'sales',st:0,d:'Rédaction',p:'Emails, articles',clr:'#3b82f6',hair:'#8a5020',skin:'#f5dcc0',htype:'bob'},
|
|
{n:'Architect',rm:'consult',st:2,d:'Architecture',p:'Blueprints',clr:'#7c3aed',hair:'#2a2a3a',skin:'#e8cca0',htype:'short',glasses:1},
|
|
{n:'Planner',rm:'consult',st:1,d:'Planning',p:'Roadmaps',clr:'#7c3aed',hair:'#5a3a1a',skin:'#f5dcc0',htype:'side'},
|
|
{n:'DeerFlow',rm:'consult',st:1,d:'Research',p:'Synthèses R&D',clr:'#7c3aed',hair:'#6a4a20',skin:'#d8b080',htype:'wild'},
|
|
{n:'Critic',rm:'consult',st:1,d:'Validation',p:'Risques',clr:'#7c3aed',hair:'#3a3a4a',skin:'#e8cca0',htype:'short',glasses:1},
|
|
{n:'Executor',rm:'dev',st:3,d:'Deploy',p:'Scripts',clr:'#10b981',hair:'#22c55e',skin:'#c9956a',htype:'mohawk'},
|
|
{n:'Debugger',rm:'dev',st:3,d:'Debug',p:'Fixes',clr:'#10b981',hair:'#4a2a10',skin:'#f5dcc0',htype:'messy',glasses:1},
|
|
{n:'Reviewer',rm:'dev',st:3,d:'Code review',p:'PR reviews',clr:'#10b981',hair:'#333',skin:'#e8cca0',htype:'short'},
|
|
{n:'Designer',rm:'dev',st:2,d:'UI/UX',p:'Mockups',clr:'#10b981',hair:'#d946ef',skin:'#f5dcc0',htype:'long'},
|
|
{n:'WEDROID',rm:'dev',st:3,d:'Auto-fix',p:'DB repair',clr:'#10b981',hair:'#5a7a9a',skin:'#8a9ab0',htype:'robot'},
|
|
{n:'Simplifier',rm:'dev',st:3,d:'Refactor',p:'-40% code',clr:'#10b981',hair:'#6a4a30',skin:'#e8cca0',htype:'bun',glasses:1},
|
|
{n:'Watchdog',rm:'srv',st:6,d:'Monitor',p:'Auto-restart',clr:'#f59e0b',hair:'#8a6a30',skin:'#d8b080',htype:'ears'},
|
|
{n:'Guardian',rm:'srv',st:4,d:'Protection',p:'Lockdown',clr:'#f59e0b',hair:'#1a2a1a',skin:'#c9956a',htype:'helmet'},
|
|
{n:'Blade',rm:'srv',st:6,d:'Desktop',p:'PowerShell',clr:'#f59e0b',hair:'#1a3050',skin:'#f5dcc0',htype:'cap'},
|
|
{n:'GitMaster',rm:'srv',st:6,d:'Git flow',p:'Deploys',clr:'#f59e0b',hair:'#3a5a2a',skin:'#e8cca0',htype:'ponytail',glasses:1},
|
|
{n:'Security',rm:'sec',st:4,d:'OWASP',p:'Audits',clr:'#ef4444',hair:'#111',skin:'#c9956a',htype:'buzz'},
|
|
{n:'Verifier',rm:'sec',st:4,d:'ISO/RGPD',p:'Checks',clr:'#ef4444',hair:'#3a3a4a',skin:'#e8cca0',htype:'short',glasses:1},
|
|
{n:'QA',rm:'qa',st:5,d:'Tests E2E',p:'148 NonReg',clr:'#06b6d4',hair:'#2a3a5a',skin:'#f5dcc0',htype:'short'},
|
|
{n:'TestEng',rm:'qa',st:5,d:'CI/CD',p:'Pipelines',clr:'#06b6d4',hair:'#4a3a2a',skin:'#e8cca0',htype:'flat'},
|
|
{n:'Tracer',rm:'qa',st:5,d:'Tracing',p:'Stack traces',clr:'#06b6d4',hair:'#3a2a1a',skin:'#d8b080',htype:'short'},
|
|
{n:'Scientist',rm:'qa',st:5,d:'Benchmarks',p:'AI Bench',clr:'#06b6d4',hair:'#888',skin:'#f5dcc0',htype:'einstein',glasses:1},
|
|
{n:'Explore',rm:'pharma',st:0,d:'R&D pharma',p:'Sources HCP',clr:'#a855f7',hair:'#5a3a10',skin:'#c9956a',htype:'adventurer'},
|
|
{n:'DocSpec',rm:'pharma',st:7,d:'Documentation',p:'Templates',clr:'#a855f7',hair:'#333',skin:'#e8cca0',htype:'neat',glasses:1},
|
|
{n:'MiroFish',rm:'pharma',st:2,d:'Creative AI',p:'Brainstorm',clr:'#a855f7',hair:'#06b6d4',skin:'#f5dcc0',htype:'wavy'},
|
|
{n:'TaskMgr',rm:'ops',st:7,d:'Tâches',p:'Kanban',clr:'#eab308',hair:'#4a4a3a',skin:'#e8cca0',htype:'side'},
|
|
{n:'Brain',rm:'ops',st:2,d:'Brainstorm',p:'Idées',clr:'#eab308',hair:'#eab308',skin:'#f5dcc0',htype:'spiky'},
|
|
{n:'Intro',rm:'ops',st:5,d:'Méta-analyse',p:'Amélioration',clr:'#eab308',hair:'#a855f7',skin:'#e8cca0',htype:'glow'},
|
|
{n:'Orch',rm:'ops',st:6,d:'Orchestration',p:'Coordination',clr:'#eab308',hair:'#222',skin:'#c9956a',htype:'military'},
|
|
];
|
|
|
|
// States: sitting, go_chain, at_chain, go_back
|
|
AG.forEach((a,i)=>{a.state='sitting';a.x=0;a.y=0;a.dx=0;a.dy=0;a.cx=0;a.cy=0;
|
|
a.bob=Math.random()*6.28;a.wk=0;a.tmr=300+Math.random()*900;a.wtmr=0;
|
|
a.dir=1;a.bl=0;a.blt=100+Math.random()*250;a.task='';a.taskT=0;});
|
|
|
|
const TASKS=['📊 Rapport','📧 Email','🔧 Fix','📋 Review','🔍 Analyse','📦 Deploy','🧪 Test','📝 Doc','🛡️ Audit','🎨 Design','💡 Idée','🐛 Debug'];
|
|
|
|
function doLayout(){
|
|
const pad=10,offY=42;
|
|
// Row 1: CEO + Sales + Consulting (top)
|
|
// Row 2: Dev + Server + Security (middle)
|
|
// Row 3: QA + Pharma + Ops (bottom, above chain)
|
|
const rows=[[0,1,2],[3,4,5],[6,7,8]];
|
|
const totalH=(H-offY-H*.2-30)/3;
|
|
rows.forEach((row,ri)=>{
|
|
const ws=row.map(i=>RM[i].w);
|
|
const totalW=ws.reduce((a,b)=>a+b,0);
|
|
const scale=(W-pad*(row.length+1))/totalW;
|
|
let cx=pad;
|
|
row.forEach((idx,ci)=>{
|
|
const r=RM[idx];r.pw=r.w*scale;r.ph=totalH-pad;
|
|
r.sx=cx;r.sy=offY+ri*(totalH);cx+=r.pw+pad;
|
|
});
|
|
});
|
|
// Chain
|
|
const chainY=H*.84;
|
|
const sg=(W-60)/CH.length;
|
|
CH.forEach((s,i)=>{s.x=40+i*sg+sg/2;s.y=chainY;});
|
|
// Agent desk positions
|
|
AG.forEach(a=>{
|
|
const rm=RM.find(r=>r.id===a.rm);if(!rm)return;
|
|
const mates=AG.filter(b=>b.rm===a.rm);const mi=mates.indexOf(a);
|
|
const cols=Math.max(Math.ceil(mates.length/2),1);
|
|
const row=Math.floor(mi/cols),col=mi%cols;
|
|
a.dx=rm.sx+20+col*Math.min((rm.pw-40)/Math.max(cols-1,1),48);
|
|
a.dy=rm.sy+28+row*32;
|
|
if(a.state==='sitting'){a.x=a.dx;a.y=a.dy;}
|
|
const st=CH[a.st];if(st){a.cx=st.x+(Math.random()-.5)*16;a.cy=st.y-8;}
|
|
});
|
|
}
|
|
resize();
|
|
|
|
// ═══ DRAW ROOM (bright, 3D box) ═══
|
|
function drawRoom(r){
|
|
const d=5;// 3D depth
|
|
const fc=FLOOR[r.id]||'#f0f4fa';
|
|
const wc=WALL[r.id]||'#aaa';
|
|
// 3D sides
|
|
X.fillStyle=wc+'25';
|
|
X.beginPath();X.moveTo(r.sx+r.pw,r.sy);X.lineTo(r.sx+r.pw+d,r.sy+d);X.lineTo(r.sx+r.pw+d,r.sy+r.ph+d);X.lineTo(r.sx+r.pw,r.sy+r.ph);X.closePath();X.fill();
|
|
X.beginPath();X.moveTo(r.sx,r.sy+r.ph);X.lineTo(r.sx+d,r.sy+r.ph+d);X.lineTo(r.sx+r.pw+d,r.sy+r.ph+d);X.lineTo(r.sx+r.pw,r.sy+r.ph);X.closePath();X.fill();
|
|
// Main face
|
|
X.fillStyle=fc;X.beginPath();X.roundRect(r.sx,r.sy,r.pw,r.ph,10);X.fill();
|
|
// Border
|
|
X.strokeStyle=wc+'40';X.lineWidth=2;X.beginPath();X.roundRect(r.sx,r.sy,r.pw,r.ph,10);X.stroke();
|
|
// Top accent bar
|
|
X.fillStyle=wc;X.beginPath();X.roundRect(r.sx,r.sy,r.pw,4,[10,10,0,0]);X.fill();
|
|
// Label
|
|
X.font='800 10px Nunito';X.fillStyle=wc;X.textAlign='left';
|
|
X.fillText(r.label,r.sx+8,r.sy+16);
|
|
// Furniture decorations
|
|
if(r.id==='srv'){// Server racks
|
|
for(let i=0;i<3;i++){const rx=r.sx+r.pw-16-i*14;
|
|
X.fillStyle='#e2e8f0';X.fillRect(rx,r.sy+20,10,r.ph-28);
|
|
X.strokeStyle='#cbd5e1';X.lineWidth=.5;X.strokeRect(rx,r.sy+20,10,r.ph-28);
|
|
for(let j=0;j<5;j++){X.fillStyle=Math.sin(fr*.06+i+j)>.2?'#22c55e':'#f59e0b';
|
|
X.beginPath();X.arc(rx+3,r.sy+26+j*7,1.5,0,6.28);X.fill();}}
|
|
}
|
|
if(r.id==='ceo'){// Plant
|
|
X.fillStyle='#c2956b';X.fillRect(r.sx+r.pw-18,r.sy+r.ph-16,8,10);
|
|
X.fillStyle='#22c55e';X.beginPath();X.arc(r.sx+r.pw-14,r.sy+r.ph-20,8,Math.PI,.1);X.fill();
|
|
X.fillStyle='#16a34a';X.beginPath();X.arc(r.sx+r.pw-14,r.sy+r.ph-24,6,Math.PI,.2);X.fill();
|
|
}
|
|
// Whiteboard
|
|
if(r.id==='consult'||r.id==='ops'){
|
|
X.fillStyle='#fff';X.fillRect(r.sx+r.pw-42,r.sy+18,34,20);
|
|
X.strokeStyle='#cbd5e1';X.lineWidth=1;X.strokeRect(r.sx+r.pw-42,r.sy+18,34,20);
|
|
X.fillStyle=wc+'30';X.fillRect(r.sx+r.pw-38,r.sy+22,12,3);X.fillRect(r.sx+r.pw-38,r.sy+28,20,2);X.fillRect(r.sx+r.pw-38,r.sy+33,16,2);
|
|
}
|
|
}
|
|
|
|
// ═══ DRAW DESK ═══
|
|
function drawDesk(x,y,clr,occ){
|
|
// Chair
|
|
X.fillStyle=occ?clr+'30':'#e2e8f0';
|
|
X.beginPath();X.arc(x,y+8,5,0,6.28);X.fill();
|
|
// Table
|
|
X.fillStyle='#f8fafc';X.shadowColor='#00000010';X.shadowBlur=4;
|
|
X.beginPath();X.roundRect(x-12,y-2,24,8,3);X.fill();X.shadowBlur=0;
|
|
X.strokeStyle='#e2e8f0';X.lineWidth=.8;X.beginPath();X.roundRect(x-12,y-2,24,8,3);X.stroke();
|
|
// Monitor
|
|
X.fillStyle=occ?clr+'20':'#f1f5f9';
|
|
X.fillRect(x-5,y-10,10,7);
|
|
X.strokeStyle=occ?clr+'50':'#e2e8f0';X.lineWidth=.6;X.strokeRect(x-5,y-10,10,7);
|
|
// Stand
|
|
X.fillStyle='#cbd5e1';X.fillRect(x-1,y-3,2,2);
|
|
}
|
|
|
|
// ═══ CHIBI CHARACTER (Good Job! style) ═══
|
|
function drawC(a){
|
|
const isH=a===hov,sit=a.state==='sitting';
|
|
const sc=isH?1.15:1;
|
|
const bob=sit?0:Math.sin(a.bob)*1.5;
|
|
const lsw=sit?0:Math.sin(a.wk)*3;
|
|
X.save();X.translate(a.x,a.y+bob);X.scale(sc*a.dir,sc);
|
|
// Shadow (soft circle)
|
|
X.fillStyle='#00000012';X.beginPath();X.ellipse(0,sit?5:10,8,3,0,0,6.28);X.fill();
|
|
if(isH){X.shadowColor=a.clr;X.shadowBlur=14;}
|
|
const oy=sit?-1:0;
|
|
// ═══ BODY (pill shape) ═══
|
|
const bg=X.createLinearGradient(0,oy-5,0,oy+4);bg.addColorStop(0,a.clr);bg.addColorStop(1,a.clr+'cc');
|
|
X.fillStyle=bg;X.beginPath();X.roundRect(-6,oy-5,12,10,[5,5,3,3]);X.fill();
|
|
// Highlight
|
|
X.fillStyle='#ffffff20';X.beginPath();X.roundRect(-4,oy-4,4,7,[2,0,0,2]);X.fill();
|
|
// ═══ LEGS ═══
|
|
if(!sit){
|
|
X.fillStyle=a.clr+'dd';
|
|
X.save();X.translate(-2.5,oy+4);X.rotate(lsw*.06);X.beginPath();X.roundRect(-1.5,0,3,7,1.5);X.fill();X.restore();
|
|
X.save();X.translate(2.5,oy+4);X.rotate(-lsw*.06);X.beginPath();X.roundRect(-1.5,0,3,7,1.5);X.fill();X.restore();
|
|
// Shoes
|
|
X.fillStyle='#fff';
|
|
X.beginPath();X.roundRect(-5+lsw*.2,oy+10,4.5,2.5,[0,0,2,2]);X.fill();
|
|
X.beginPath();X.roundRect(.5-lsw*.2,oy+10,4.5,2.5,[0,0,2,2]);X.fill();
|
|
}
|
|
// ═══ ARMS ═══
|
|
X.fillStyle=a.skin;
|
|
const asw=sit?0:Math.sin(a.wk+.5)*.15;
|
|
X.save();X.translate(-7,oy-2);X.rotate(sit?.25:asw);X.beginPath();X.roundRect(-1.5,0,3,sit?4:7,1.5);X.fill();X.restore();
|
|
X.save();X.translate(7,oy-2);X.rotate(sit?-.25:-asw);X.beginPath();X.roundRect(-1.5,0,3,sit?4:7,1.5);X.fill();X.restore();
|
|
// ═══ HEAD (BIG round — chibi) ═══
|
|
const hy=oy-16;const hr=9;
|
|
// Head shadow
|
|
X.fillStyle='#00000008';X.beginPath();X.arc(0,hy+hr+2,hr*.7,0,Math.PI);X.fill();
|
|
// Head
|
|
X.fillStyle=a.skin;X.beginPath();X.arc(0,hy,hr,0,6.28);X.fill();
|
|
// Cheeks
|
|
X.fillStyle='#ff888815';X.beginPath();X.arc(-5,hy+3,2.5,0,6.28);X.fill();X.beginPath();X.arc(5,hy+3,2.5,0,6.28);X.fill();
|
|
// ═══ HAIR ═══
|
|
X.fillStyle=a.hair;
|
|
switch(a.htype){
|
|
case'slick':X.beginPath();X.arc(0,hy-1,hr+.5,.6,Math.PI+.4);X.fill();X.fillRect(-7,hy-6,14,5);break;
|
|
case'short':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();break;
|
|
case'curly':for(let i=0;i<9;i++){const ag=-2.3+i*.5;X.beginPath();X.arc(Math.cos(ag)*8,hy-2+Math.sin(ag)*7,3,0,6.28);X.fill();}break;
|
|
case'bob':X.beginPath();X.arc(0,hy-1,hr+.5,.15,Math.PI);X.fill();X.fillRect(-10,hy,4,7);X.fillRect(6,hy,4,7);break;
|
|
case'side':X.beginPath();X.arc(0,hy-1,hr+.5,.4,Math.PI-.2);X.fill();X.fillRect(-10,hy-2,5,7);break;
|
|
case'wild':X.beginPath();X.arc(0,hy-2,hr+2,.15,Math.PI);X.fill();X.beginPath();X.arc(-10,hy-1,3.5,0,6.28);X.fill();X.beginPath();X.arc(10,hy-1,3.5,0,6.28);X.fill();break;
|
|
case'mohawk':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();for(let i=0;i<4;i++)X.fillRect(-1.5,hy-10-i*2,3,4);break;
|
|
case'messy':X.beginPath();X.arc(0,hy-2,hr+1,.3,Math.PI-.1);X.fill();for(let i=0;i<5;i++)X.fillRect(-6+i*3,hy-9-Math.sin(i)*2,3,4);break;
|
|
case'long':X.beginPath();X.arc(0,hy-1,hr+.5,.15,Math.PI);X.fill();X.fillRect(-10,hy-1,4,10);X.fillRect(6,hy-1,4,10);break;
|
|
case'bun':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();X.beginPath();X.arc(0,hy-9,4,0,6.28);X.fill();break;
|
|
case'ponytail':X.beginPath();X.arc(0,hy-1,hr+.5,.4,Math.PI-.2);X.fill();X.fillRect(6,hy,2.5,10);X.beginPath();X.arc(7.2,hy+10,2.5,0,6.28);X.fill();break;
|
|
case'ears':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();
|
|
X.beginPath();X.moveTo(-7,hy-4);X.lineTo(-12,hy-12);X.lineTo(-4,hy-2);X.fill();
|
|
X.beginPath();X.moveTo(7,hy-4);X.lineTo(12,hy-12);X.lineTo(4,hy-2);X.fill();break;
|
|
case'helmet':X.fillStyle='#5a8a5a';X.beginPath();X.arc(0,hy-1,hr+1.5,.25,Math.PI-.1);X.fill();X.fillRect(-10,hy,.5,20);X.fillRect(10,hy,.5,20);break;
|
|
case'cap':X.beginPath();X.arc(0,hy-1,hr+.5,.3,Math.PI-.1);X.fill();X.fillRect(-11,hy-1,22,3);X.fillRect(-13,hy,8,2.5);break;
|
|
case'robot':X.fillStyle='#94a3b8';X.beginPath();X.roundRect(-8,hy-7,16,14,3);X.fill();
|
|
X.fillStyle='#64748b';X.fillRect(-6,hy-3,12,4);
|
|
X.strokeStyle='#94a3b8';X.lineWidth=1.5;X.beginPath();X.moveTo(0,hy-7);X.lineTo(0,hy-11);X.stroke();
|
|
X.fillStyle='#ef4444';X.beginPath();X.arc(0,hy-11,2,0,6.28);X.fill();break;
|
|
case'buzz':X.beginPath();X.arc(0,hy-1,hr+1,.4,Math.PI-.2);X.fill();break;
|
|
case'flat':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();X.fillRect(-8,hy-4,16,2.5);break;
|
|
case'einstein':X.beginPath();X.arc(0,hy-2,hr+2,.15,Math.PI);X.fill();X.beginPath();X.arc(-10,hy-1,4,0,6.28);X.fill();X.beginPath();X.arc(10,hy-1,4,0,6.28);X.fill();break;
|
|
case'adventurer':X.beginPath();X.arc(0,hy-1,hr+.5,.4,Math.PI-.2);X.fill();X.fillStyle=a.hair+'88';X.fillRect(-11,hy-2,22,3);X.fillRect(-13,hy-1,9,2.5);break;
|
|
case'neat':X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();break;
|
|
case'wavy':for(let i=0;i<7;i++){const ag=-2+i*.6;X.beginPath();X.arc(Math.cos(ag)*8,hy-2+Math.sin(ag)*6+Math.sin(i)*1.5,2.5,0,6.28);X.fill();}break;
|
|
case'spiky':for(let i=0;i<6;i++){const ag=-1.6+i*.55;X.beginPath();X.moveTo(Math.cos(ag)*7,hy-2+Math.sin(ag)*6);X.lineTo(Math.cos(ag)*(hr+5),hy-3+Math.sin(ag)*(hr+3));X.lineTo(Math.cos(ag+.25)*7,hy-2+Math.sin(ag+.25)*6);X.fill();}break;
|
|
case'glow':X.beginPath();X.arc(0,hy-1,hr+.5,.3,Math.PI-.1);X.fill();X.fillStyle=a.hair+'18';X.beginPath();X.arc(0,hy-3,16,0,6.28);X.fill();break;
|
|
case'military':X.beginPath();X.arc(0,hy-1,hr+.5,.4,Math.PI-.2);X.fill();X.fillRect(-8,hy-2,16,2);break;
|
|
default:X.beginPath();X.arc(0,hy-1,hr+.5,.5,Math.PI-.3);X.fill();
|
|
}
|
|
// ═══ EYES ═══
|
|
if(a.htype!=='robot'){
|
|
if(a.bl<=0){
|
|
X.fillStyle='#fff';X.beginPath();X.ellipse(-3,hy,3,3.5,0,0,6.28);X.fill();X.beginPath();X.ellipse(3,hy,3,3.5,0,0,6.28);X.fill();
|
|
X.fillStyle='#1a1a3a';X.beginPath();X.arc(-2.5,hy+.5,1.8,0,6.28);X.fill();X.beginPath();X.arc(3.5,hy+.5,1.8,0,6.28);X.fill();
|
|
X.fillStyle='#000';X.beginPath();X.arc(-2.5,hy+.8,1,0,6.28);X.fill();X.beginPath();X.arc(3.5,hy+.8,1,0,6.28);X.fill();
|
|
X.fillStyle='#fff';X.beginPath();X.arc(-3.2,hy-.8,.7,0,6.28);X.fill();X.beginPath();X.arc(2.8,hy-.8,.7,0,6.28);X.fill();
|
|
} else {X.strokeStyle='#333';X.lineWidth=1.5;X.lineCap='round';X.beginPath();X.moveTo(-5,hy);X.lineTo(-1,hy);X.stroke();X.beginPath();X.moveTo(1,hy);X.lineTo(5,hy);X.stroke();}
|
|
if(a.glasses){X.strokeStyle='#94a3b8';X.lineWidth=.7;X.beginPath();X.arc(-3,hy,4,0,6.28);X.stroke();X.beginPath();X.arc(3,hy,4,0,6.28);X.stroke();X.beginPath();X.moveTo(-.5,hy);X.lineTo(.5,hy);X.stroke();}
|
|
// Mouth
|
|
X.strokeStyle='#d08080';X.lineWidth=.7;X.lineCap='round';X.beginPath();
|
|
if(a.state==='at_chain'){X.arc(0,hy+5.5,2,.2,Math.PI-.2);}else{X.moveTo(-1.5,hy+5.5);X.lineTo(1.5,hy+5.5);}X.stroke();
|
|
} else {
|
|
X.fillStyle=a.state!=='sitting'?'#22c55e':'#3b82f6';
|
|
X.beginPath();X.roundRect(-4,hy-1,3.5,2.5,1);X.fill();X.beginPath();X.roundRect(.5,hy-1,3.5,2.5,1);X.fill();
|
|
}
|
|
// Name
|
|
X.font=`${isH?'800':'600'} ${isH?8:6.5}px Nunito`;
|
|
X.fillStyle=isH?'#2a2a4a':a.state!=='sitting'?a.clr:'#8a9ab8';X.textAlign='center';
|
|
X.fillText(a.n,0,sit?15:20);
|
|
// Active indicator
|
|
if(a.state!=='sitting'){X.fillStyle=a.clr;X.beginPath();X.arc(0,oy-22,2.5,0,6.28);X.fill();}
|
|
// Task bubble
|
|
if(a.taskT>0){const ba=Math.min(a.taskT/15,1);X.globalAlpha=ba;
|
|
X.fillStyle='#fff';X.shadowColor='#00000015';X.shadowBlur=6;
|
|
const bw=a.task.length*4.5+10;X.beginPath();X.roundRect(-bw/2,oy-38,bw,15,8);X.fill();X.shadowBlur=0;
|
|
X.fillStyle='#fff';X.beginPath();X.moveTo(-2,oy-23);X.lineTo(2,oy-23);X.lineTo(0,oy-20);X.closePath();X.fill();
|
|
X.font='600 7px Nunito';X.fillStyle='#2a2a4a';X.fillText(a.task,0,oy-28);X.globalAlpha=1;}
|
|
X.restore();
|
|
}
|
|
|
|
// ═══ DRAW CHAIN ═══
|
|
function drawChain(){
|
|
const y=CH[0].y;
|
|
// Belt
|
|
X.fillStyle='#f1f5f9';X.shadowColor='#00000010';X.shadowBlur=8;
|
|
X.beginPath();X.roundRect(20,y-15,W-40,30,12);X.fill();X.shadowBlur=0;
|
|
X.strokeStyle='#e2e8f0';X.lineWidth=1.5;X.beginPath();X.roundRect(20,y-15,W-40,30,12);X.stroke();
|
|
// Belt stripes
|
|
const off=(fr*.8)%16;X.strokeStyle='#e2e8f020';X.lineWidth=.4;
|
|
for(let x=25-off;x<W-25;x+=16){X.beginPath();X.moveTo(x,y-14);X.lineTo(x,y+14);X.stroke();}
|
|
// Stations
|
|
CH.forEach((s,i)=>{
|
|
X.fillStyle=s.c+'18';X.beginPath();X.arc(s.x,y,16,0,6.28);X.fill();
|
|
X.fillStyle='#fff';X.beginPath();X.arc(s.x,y,6,0,6.28);X.fill();
|
|
X.fillStyle=s.c;X.beginPath();X.arc(s.x,y,4,0,6.28);X.fill();
|
|
X.font='800 7px Nunito';X.textAlign='center';X.fillStyle=s.c;X.fillText(s.l,s.x,y+24);
|
|
if(i<CH.length-1){const n=CH[i+1];
|
|
X.strokeStyle='#cbd5e1';X.lineWidth=1;X.beginPath();X.moveTo(s.x+8,y);X.lineTo(n.x-8,y);X.stroke();
|
|
const dt=((fr*1.2+i*25)%(n.x-s.x-16));X.fillStyle=s.c+'50';X.beginPath();X.arc(s.x+8+dt,y,2,0,6.28);X.fill();}
|
|
});
|
|
}
|
|
|
|
// ═══ UPDATE ═══
|
|
function upd(dt){
|
|
fr++;let ac=0;
|
|
AG.forEach(a=>{
|
|
a.bob+=dt*(a.state==='sitting'?1:3.5);a.blt-=dt*60;
|
|
if(a.blt<=0){a.bl=4;a.blt=100+Math.random()*250;}if(a.bl>0)a.bl-=dt*60;if(a.taskT>0)a.taskT-=dt*20;
|
|
switch(a.state){
|
|
case'sitting':a.tmr-=dt*60;if(a.tmr<=0){a.state='go_chain';a.wk=0;a.task=TASKS[Math.floor(Math.random()*TASKS.length)];a.taskT=40;}break;
|
|
case'go_chain':a.wk+=dt*7;ac++;{const dx=a.cx-a.x,dy=a.cy-a.y,d=Math.sqrt(dx*dx+dy*dy);
|
|
if(d>2){a.x+=dx/d*85*dt;a.y+=dy/d*85*dt;a.dir=dx>0?1:-1;}
|
|
else{a.state='at_chain';a.wtmr=50+Math.random()*80;a.taskT=35;tasks++;}}break;
|
|
case'at_chain':a.wk+=dt*2;ac++;a.wtmr-=dt*60;if(a.wtmr<=0)a.state='go_back';break;
|
|
case'go_back':a.wk+=dt*7;ac++;{const dx=a.dx-a.x,dy=a.dy-a.y,d=Math.sqrt(dx*dx+dy*dy);
|
|
if(d>2){a.x+=dx/d*85*dt;a.y+=dy/d*85*dt;a.dir=dx>0?1:-1;}
|
|
else{a.state='sitting';a.x=a.dx;a.y=a.dy;a.dir=1;a.tmr=400+Math.random()*1000;}}break;
|
|
}
|
|
});document.getElementById('ac').textContent=ac;document.getElementById('tc').textContent=tasks;}
|
|
|
|
function hit(){hov=null;AG.forEach(a=>{if(Math.abs(mx-a.x)<10&&Math.abs(my-a.y)<16)hov=a;});
|
|
const t=document.getElementById('tip');if(hov){t.style.display='block';t.style.left=Math.min(mx+14,W-250)+'px';t.style.top=Math.max(my-150,10)+'px';
|
|
t.style.borderColor=hov.clr;t.querySelector('.tn').textContent=hov.n;
|
|
t.querySelector('.tt').textContent=RM.find(r=>r.id===hov.rm)?.label||'';t.querySelector('.tt').style.color=hov.clr;
|
|
t.querySelector('.td').textContent=hov.d;t.querySelector('.tp').textContent='→ '+hov.p;
|
|
const sm={sitting:'💤 Au bureau',go_chain:'🚶 → Production',at_chain:'⚙️ En cours...',go_back:'✅ Retour'};
|
|
t.querySelector('.st').textContent=sm[hov.state];t.querySelector('.st').style.color=hov.state==='sitting'?'#94a3b8':'#16a34a';
|
|
}else t.style.display='none';}
|
|
|
|
let lt=0;function loop(t){const dt=Math.min((t-lt)/1000,.04);lt=t;
|
|
X.fillStyle=BG;X.fillRect(0,0,W,H);
|
|
RM.forEach(r=>drawRoom(r));
|
|
AG.forEach(a=>{const rm=RM.find(r=>r.id===a.rm);if(rm)drawDesk(a.dx,a.dy,WALL[a.rm],a.state==='sitting');});
|
|
drawChain();upd(dt);
|
|
// Path lines
|
|
AG.filter(a=>a.state==='go_chain'||a.state==='go_back').forEach(a=>{X.strokeStyle=a.clr+'15';X.lineWidth=1;X.setLineDash([2,4]);X.beginPath();X.moveTo(a.dx,a.dy);X.lineTo(a.x,a.y);X.stroke();X.setLineDash([]);});
|
|
[...AG].sort((a,b)=>a.y-b.y).forEach(a=>drawC(a));hit();requestAnimationFrame(loop);}
|
|
|
|
C.addEventListener('mousemove',e=>{mx=e.clientX;my=e.clientY;C.style.cursor=hov?'pointer':'default'});
|
|
C.addEventListener('mouseleave',()=>{mx=my=-1});
|
|
requestAnimationFrame(loop);
|
|
</script>
|
|
<!-- CARTO_REMOVED -->
|
|
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
|
|
<script>
|
|
(function(){
|
|
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
|
|
var d = document;
|
|
var m = d.createElement('div');
|
|
m.id = 'opus-udrill';
|
|
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
|
|
var inner = d.createElement('div');
|
|
inner.id = 'opus-udrill-in';
|
|
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
|
|
inner.addEventListener('click', function(e){ e.stopPropagation(); });
|
|
m.appendChild(inner);
|
|
m.addEventListener('click', function(){ m.style.display='none'; });
|
|
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
|
|
(d.body || d.documentElement).appendChild(m);
|
|
|
|
function openCard(card) {
|
|
// Clone card content + show close btn + increase font-size
|
|
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
|
|
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
|
|
inner.innerHTML = html;
|
|
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
|
|
m.style.display = 'flex';
|
|
}
|
|
|
|
function wire(root) {
|
|
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
|
|
var cards = root.querySelectorAll(sels);
|
|
for (var i = 0; i < cards.length; i++) {
|
|
var c = cards[i];
|
|
if (c.__opusWired) continue;
|
|
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
|
|
var r = c.getBoundingClientRect();
|
|
if (r.width < 60 || r.height < 40) continue;
|
|
c.__opusWired = true;
|
|
c.style.cursor = 'pointer';
|
|
c.setAttribute('role','button');
|
|
c.setAttribute('tabindex','0');
|
|
c.addEventListener('click', function(ev){
|
|
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
|
|
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
|
|
if (ev.target.closest('a,button,input,select')) return;
|
|
ev.preventDefault(); ev.stopPropagation();
|
|
openCard(this);
|
|
});
|
|
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
|
|
}
|
|
}
|
|
|
|
// Initial + mutation observer
|
|
var initRun = function(){ wire(d.body || d.documentElement); };
|
|
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
|
|
else initRun();
|
|
var mo = new MutationObserver(function(muts){
|
|
var newCard = false;
|
|
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
|
|
if (newCard) initRun();
|
|
});
|
|
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
|
|
})();
|
|
</script>
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
|
|
|
|
</body>
|
|
</html>
|