Files
html/bpmn-studio-live.html

219 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8"><title>BPMN Studio Live — WEVIA EM</title>
<link rel="stylesheet" href="https://unpkg.com/bpmn-js@13.0.0/dist/assets/diagram-js.css">
<link rel="stylesheet" href="https://unpkg.com/bpmn-js@13.0.0/dist/assets/bpmn-font/css/bpmn.css">
<style>
*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,sans-serif;background:#0a0e1a;color:#e2e8f0}
.hd{background:linear-gradient(135deg,#3b82f6,#1e40af);padding:16px 24px;display:flex;justify-content:space-between;align-items:center}
.hd h1{color:white;font-size:20px}.ctrls{display:flex;gap:8px}
.btn{background:#0a0e1a;color:white;border:1px solid #1e40af;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:12px}
.btn:hover{background:#1e40af}
.wrap{display:grid;grid-template-columns:260px 1fr;height:calc(100vh - 60px)}
.side{background:#111827;border-right:1px solid #1e293b;overflow-y:auto;padding:12px}
.side h3{font-size:11px;color:#60a5fa;text-transform:uppercase;margin:10px 0 6px;letter-spacing:1px}
.r-item{padding:8px;margin:4px 0;background:#0a0e1a;border:1px solid #1e293b;border-radius:6px;cursor:pointer;font-size:12px}
.r-item:hover{border-color:#60a5fa}.r-item .dept{color:#64748b;font-size:10px;margin-top:2px}
#canvas{background:#fff;height:100%}
.toolbar{position:absolute;top:80px;right:20px;background:#111827;border:1px solid #1e293b;border-radius:8px;padding:8px;display:flex;flex-direction:column;gap:4px}
/* === WEVIA Gemini Rolling v2 VISIBLE Enrichment (wave 306 batch) === */
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card,.widget,.stat,.box{position:relative!important}
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card{animation:geV2Entrance .8s cubic-bezier(.34,1.56,.64,1) backwards}
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.09s}
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.18s}
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.27s}
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.36s}
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.45s}
@keyframes geV2Entrance{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card,.widget{border:1px solid transparent!important;box-shadow:0 0 0 1px rgba(236,72,153,.15),0 4px 16px rgba(0,0,0,.25)!important;transition:box-shadow .4s,transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s!important}
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover,.hub-card:hover{transform:translateY(-6px) scale(1.03)!important;filter:brightness(1.2)!important;box-shadow:0 0 0 2px rgba(236,72,153,.6),0 12px 32px rgba(236,72,153,.25),0 0 24px rgba(78,205,196,.2)!important}
.kpi::before,[class*="card"]::before,.stat-card::before,.metric-card::before,.hub-card::before{content:"";position:absolute;top:12px;right:12px;width:10px;height:10px;border-radius:50%;background:radial-gradient(circle,#2ed573,#1a9a4e);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5);animation:geV2Pulse 1.6s ease-out infinite;z-index:100;pointer-events:none}
@keyframes geV2Pulse{0%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}50%{transform:scale(1.4);box-shadow:0 0 20px #2ed573,0 0 40px rgba(46,213,115,.8)}100%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}}
body::after{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 70% 30%,transparent 40%,rgba(236,72,153,.06) 100%),radial-gradient(ellipse at 30% 70%,transparent 40%,rgba(78,205,196,.04) 100%);animation:geV2Ambient 10s ease-in-out infinite;z-index:0}
@keyframes geV2Ambient{0%,100%{opacity:.5}50%{opacity:1}}
h1,.header-title,.main-title,.hub-title,.page-title{background-image:linear-gradient(90deg,currentColor 0%,currentColor 40%,rgba(236,72,153,1) 50%,currentColor 60%,currentColor 100%)!important;background-size:200% auto!important;-webkit-background-clip:text!important;background-clip:text!important;-webkit-text-fill-color:transparent!important;animation:geV2Shimmer 5s linear infinite!important}
@keyframes geV2Shimmer{0%{background-position:200% center}100%{background-position:-200% center}}
/* Doctrine zero chevauchement - hide common offenders */
.opus-x-btn,.toggle-top-right-btn,.fab-corner{display:none!important}
/* === end WEVIA Gemini Rolling v2 batch === */
</style><!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-142452 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
<!-- DOCTRINE-222-KILL-PULSED60 -->
<style>
@keyframes pulseD60 { 0%,100%,50% { opacity: 1; transform: scale(1); } }
.pulse, .live-indicator, .active, .online { animation: none !important; }
</style>
<!-- END-DOCTRINE-222 -->
</head><body>
<div class="hd"><h1>🔄 BPMN Studio Live — Canvas + 110 Routines</h1>
<div class="ctrls">
<button class="btn" onclick="newD()"> Nouveau</button>
<button class="btn" onclick="saveD()">💾 Sauvegarder</button>
<button class="btn" onclick="exportXML()">📤 Export XML</button>
</div></div>
<div class="wrap">
<div class="side">
<h3>Routines existantes</h3>
<input id="f" placeholder="Filtrer..." oninput="filter()" style="width:100%;padding:6px;background:#0a0e1a;border:1px solid #1e293b;color:#e2e8f0;border-radius:4px;margin-bottom:10px;font-size:11px"/>
<div id="list">Chargement...</div>
</div>
<div id="canvas"></div>
</div>
<script src="https://unpkg.com/bpmn-js@13.0.0/dist/bpmn-modeler.development.js"></script>
<script>
let ROUTINES=[],modeler;
const INITIAL_XML=`<?xml version="1.0" encoding="UTF-8"?><bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"><bpmn:process id="Process_1" isExecutable="false"><bpmn:startEvent id="StartEvent_1"/></bpmn:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"><bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"><dc:Bounds x="175" y="100" width="36" height="36"/></bpmndi:BPMNShape></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></bpmn:definitions>`;
async function init(){
modeler = new BpmnJS({container:'#canvas'});
await modeler.importXML(INITIAL_XML);
modeler.get('canvas').zoom('fit-viewport');
const r = await fetch('/api/em/bpmn-routines?tenant=weval');
const d = await r.json();
ROUTINES = d.routines||[];
render(ROUTINES);
}
function render(items){
document.getElementById('list').innerHTML = items.map(r=>`<div class="r-item" onclick="loadR(${r.id})">${r.name}<div class="dept">[${r.dept}] ${r.sla_hours}h SLA</div></div>`).join('');
}
function filter(){const q=document.getElementById('f').value.toLowerCase();render(ROUTINES.filter(r=>r.name.toLowerCase().includes(q)||r.dept.toLowerCase().includes(q)))}
async function loadR(id){
const r = ROUTINES.find(x=>x.id===id); if(!r)return;
const steps = r.steps||[];
// Generate BPMN XML from steps
const nodes = steps.map((s,i)=>`<bpmn:task id="Task_${i}" name="${s.replace(/&/g,'&amp;').replace(/</g,'&lt;')}"/>`).join('');
const flows = steps.map((s,i)=>i<steps.length-1?`<bpmn:sequenceFlow id="Flow_${i}" sourceRef="${i===0?'Start_1':'Task_'+(i-1)}" targetRef="Task_${i}"/>`:`<bpmn:sequenceFlow id="FlowEnd" sourceRef="Task_${i-1}" targetRef="End_1"/>`).join('');
const shapes = steps.map((s,i)=>`<bpmndi:BPMNShape id="Shape_Task_${i}" bpmnElement="Task_${i}"><dc:Bounds x="${250+i*140}" y="80" width="100" height="80"/></bpmndi:BPMNShape>`).join('');
const xml = `<?xml version="1.0" encoding="UTF-8"?><bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn"><bpmn:process id="Process_${r.id}" isExecutable="false" name="${r.name.replace(/&/g,'&amp;')}"><bpmn:startEvent id="Start_1" name="Start"/>${nodes}<bpmn:endEvent id="End_1" name="End"/>${flows}</bpmn:process><bpmndi:BPMNDiagram id="D_1"><bpmndi:BPMNPlane id="P_1" bpmnElement="Process_${r.id}"><bpmndi:BPMNShape id="Shape_Start_1" bpmnElement="Start_1"><dc:Bounds x="180" y="102" width="36" height="36"/></bpmndi:BPMNShape>${shapes}<bpmndi:BPMNShape id="Shape_End_1" bpmnElement="End_1"><dc:Bounds x="${250+steps.length*140}" y="102" width="36" height="36"/></bpmndi:BPMNShape></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></bpmn:definitions>`;
await modeler.importXML(xml);
modeler.get('canvas').zoom('fit-viewport');
}
async function newD(){await modeler.importXML(INITIAL_XML);modeler.get('canvas').zoom('fit-viewport')}
async function saveD(){const {xml}=await modeler.saveXML({format:true});alert('Sauvegarde à câbler sur POST /api/em/bpmn/save (wire prochaine session)\\n\\n'+xml.substring(0,200))}
async function exportXML(){const {xml}=await modeler.saveXML({format:true});const b=new Blob([xml],{type:'text/xml'});const u=URL.createObjectURL(b);const a=document.createElement('a');a.href=u;a.download='bpmn-diagram.bpmn';a.click()}
init();
</script>
<!-- === 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 === -->
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t34final) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1777045903" defer></script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body></html>