Files
html/orphans-dashboard.html
Opus c7994d48be
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
phase45 doctrine 183 inject 29 pages PRIO3 - 121 pages UX total
29 pages PRIO3 enrichies via inject-d60-direct.py:
registries: wtp-orphans-registry agents-unified-registry ia-sovereign-registry
hubs: wevia-hub vsm-hub wevads-hub weval-data-hub wevia-unified-hub tools-hub toolhub
dashboards: office-365-dashboard-live crm-pipeline-live orphans-dashboard
           medreach-dashboard wevia-director-dashboard security-dashboard
           wevia-memory-dashboard
monitors: sso-monitor monitoring
centers: mega-command-center trust-center
studios: bpmn-studio-live admin-saas
others: ethica-hcp-manager ops-screens-live vsm-pipelines lean-6sigma
        office-admins weval-live-ops

Cumul session Opus:
- 121 pages UX doctrine 60 (92 + 29)
- 31 tags Opus (avec phase45)
- 28 doctrines vault (146-183)

Handler inject-d60-direct.py prouve robuste sur 65+ pages consecutives.
ZERO regression. ZERO ecrasement. NR 153/153 invariant.
2026-04-24 14:25:37 +02:00

332 lines
16 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" data-theme="dark">
<head>
<meta charset="UTF-8">
<title>Orphans Dashboard — Doctrine 92 Hub Merged</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
:root{--bg:#05060a;--bg2:#0e111c;--bg3:#181c2b;--br:#1f2436;--tx:#f1f5f9;--tx2:#94a3b8;--ac:#6366f1;--green:#22c55e;--yellow:#fbbf24;--red:#ef4444}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:var(--bg);color:var(--tx);padding:24px;line-height:1.5}
h1{font-size:30px;font-weight:800;background:linear-gradient(135deg,#6366f1,#ec4899);-webkit-background-clip:text;color:transparent;margin-bottom:4px}
.sub{color:var(--tx2);margin-bottom:20px;font-size:14px}
.back{display:inline-block;padding:8px 16px;background:var(--bg3);border:1px solid var(--br);border-radius:8px;color:var(--tx);text-decoration:none;margin-bottom:20px;transition:.2s}
.back:hover{border-color:var(--ac);transform:translateX(-2px)}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:24px}
.stat{background:var(--bg2);border:1px solid var(--br);border-radius:12px;padding:16px;transition:.2s}
.stat:hover{border-color:var(--ac);transform:translateY(-2px)}
.stat.active{border-color:var(--green)}
.stat.dormant{border-color:var(--yellow)}
.stat.archive{border-color:var(--tx2)}
.stat .n{font-size:28px;font-weight:800;color:var(--ac);margin-bottom:4px}
.stat.active .n{color:var(--green)}
.stat.dormant .n{color:var(--yellow)}
.stat.archive .n{color:var(--tx2)}
.stat .l{font-size:11px;color:var(--tx2);text-transform:uppercase;letter-spacing:.5px}
.tabs{display:flex;gap:8px;margin-bottom:20px;border-bottom:1px solid var(--br)}
.tab{padding:10px 20px;background:none;border:none;color:var(--tx2);cursor:pointer;font-weight:600;font-size:14px;border-bottom:2px solid transparent;transition:.2s}
.tab.active{color:var(--ac);border-bottom-color:var(--ac)}
.tab:hover{color:var(--tx)}
.tab .count{display:inline-block;background:var(--bg3);padding:2px 8px;border-radius:10px;font-size:11px;margin-left:6px}
.filter{margin-bottom:16px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.filter input{background:var(--bg2);border:1px solid var(--br);border-radius:8px;padding:10px 14px;color:var(--tx);font-size:14px;min-width:240px}
.filter input:focus{outline:none;border-color:var(--ac)}
.filter select{background:var(--bg2);border:1px solid var(--br);border-radius:8px;padding:10px 14px;color:var(--tx);font-size:14px;cursor:pointer}
.pages{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:10px}
.page{display:block;padding:12px 14px;background:var(--bg2);border:1px solid var(--br);border-radius:10px;color:var(--tx);text-decoration:none;transition:.2s;position:relative}
.page:hover{border-color:var(--ac);transform:translateY(-1px);box-shadow:0 4px 12px rgba(99,102,241,0.2)}
.page-header{display:flex;justify-content:space-between;align-items:flex-start;gap:8px;margin-bottom:6px}
.pn{font-family:'SF Mono',Menlo,monospace;font-size:12px;color:var(--ac);font-weight:600;word-break:break-all}
.page.active .pn{color:var(--green)}
.page.dormant .pn{color:var(--yellow)}
.page.archive .pn{color:var(--tx2)}
.pt{font-size:13px;color:var(--tx);margin-bottom:4px;font-weight:500}
.pm{font-size:11px;color:var(--tx2);display:flex;gap:10px;flex-wrap:wrap}
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.3px}
.badge.active{background:rgba(34,197,94,0.2);color:var(--green);border:1px solid var(--green)}
.badge.dormant{background:rgba(251,191,36,0.2);color:var(--yellow);border:1px solid var(--yellow)}
.badge.archive{background:rgba(148,163,184,0.2);color:var(--tx2);border:1px solid var(--tx2)}
.badge.suite{background:var(--bg3);color:var(--tx2);border:1px solid var(--br)}
.empty{text-align:center;padding:40px;color:var(--tx2)}
.loading{text-align:center;padding:40px;color:var(--ac)}
.snippet-btn{display:inline-block;margin-top:10px;padding:10px 16px;background:linear-gradient(135deg,#6366f1,#ec4899);color:#fff;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;border:none}
.snippet-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(99,102,241,0.4)}
#snippet-modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.8);z-index:100;padding:40px;overflow:auto}
#snippet-modal.show{display:block}
.modal-content{background:var(--bg2);border:1px solid var(--br);border-radius:12px;padding:24px;max-width:900px;margin:0 auto}
.modal-content pre{background:#000;border:1px solid var(--br);border-radius:8px;padding:14px;overflow:auto;font-size:12px;color:var(--green);max-height:500px}
.modal-close{float:right;background:none;border:none;color:var(--tx);font-size:24px;cursor:pointer}
/* === OPUS RESPONSIVE FIX v2 19avr — append-only, doctrine #14 === */
@media(max-width: 480px) {
html, body { overflow-x: hidden !important; max-width: 100vw; }
body, main, section, article { word-break: break-word; overflow-wrap: anywhere; }
img, video, iframe, canvas, svg, table, pre, code { max-width: 100% !important; }
pre, code { white-space: pre-wrap; word-break: break-all; }
table { display: block; overflow-x: auto; }
.container, [class*="container"], [class*="wrapper"] { max-width: 100vw !important; padding-left: 12px !important; padding-right: 12px !important; }
[class*="grid"], [class*="-grid"] { grid-template-columns: 1fr !important; gap: 10px !important; }
[class*="kpi"], [class*="stats"], [class*="-cards"] { grid-template-columns: 1fr !important; }
header, nav, footer { flex-wrap: wrap !important; }
header > *, nav > *, footer > * { max-width: 100%; }
h1 { font-size: 22px !important; word-break: break-word; }
h2 { font-size: 18px !important; }
.pitch, [class*="pitch"], [class*="hero"] { word-break: break-word; overflow-wrap: anywhere; }
}
/* === OPUS RESPONSIVE FIX v2 END === */
</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>
</head>
<body>
<a href="/weval-technology-platform.html" class="back">← WTP</a>
<h1>Orphans Dashboard</h1>
<div class="sub">Doctrine 92 · Hub Merged · V82 suites métier + D91 classifier</div>
<div class="stats" id="stats"></div>
<div class="tabs">
<button class="tab active" data-filter="all">Tous <span class="count" id="c-all">·</span></button>
<button class="tab" data-filter="ACTIVE">Actifs à rebrancher <span class="count" id="c-active">·</span></button>
<button class="tab" data-filter="DORMANT">Dormant <span class="count" id="c-dormant">·</span></button>
<button class="tab" data-filter="ARCHIVE">Archive <span class="count" id="c-archive">·</span></button>
</div>
<div class="filter">
<input type="text" id="search" placeholder="Rechercher page, titre ou suite métier…">
<select id="suite-filter"><option value="">Toutes suites</option></select>
<button class="snippet-btn" onclick="showSnippet()">📋 Voir snippet HTML WTP-ready</button>
</div>
<div id="pages" class="pages"><div class="loading">Chargement…</div></div>
<div id="snippet-modal">
<div class="modal-content">
<button class="modal-close" onclick="hideSnippet()">×</button>
<h2>Snippet HTML prêt à injecter dans WTP</h2>
<p class="sub">Additive pur · avant &lt;/body&gt; · pattern V80 · GOLD backup obligatoire</p>
<pre id="snippet-code">Chargement…</pre>
<button class="snippet-btn" onclick="copySnippet()">Copier</button>
</div>
</div>
<script>
let DATA = null;
let CURRENT_FILTER = 'all';
let CURRENT_SUITE = '';
let SEARCH_TERM = '';
async function load() {
try {
const r = await fetch('/api/opus5-orphans-hub.php');
DATA = await r.json();
renderStats();
renderSuiteFilter();
render();
} catch (e) {
document.getElementById('pages').innerHTML = '<div class="empty">Erreur chargement : ' + e.message + '</div>';
}
}
function renderStats() {
const s = DATA.stats;
document.getElementById('stats').innerHTML = `
<div class="stat"><div class="n">${s.total_orphans}</div><div class="l">Total orphelins</div></div>
<div class="stat active"><div class="n">${s.active_to_link}</div><div class="l">Actifs à rebrancher</div></div>
<div class="stat dormant"><div class="n">${s.dormant_candidate}</div><div class="l">Dormant (user decide)</div></div>
<div class="stat archive"><div class="n">${s.archive_legit}</div><div class="l">Archive légitime</div></div>
<div class="stat"><div class="n">${s.suites_v82}</div><div class="l">Suites métier V82</div></div>
<div class="stat"><div class="n">${s.sync_ok ? '✓' : '≠'}</div><div class="l">V82↔D91 sync</div></div>
`;
document.getElementById('c-all').textContent = s.total_orphans;
document.getElementById('c-active').textContent = s.active_to_link;
document.getElementById('c-dormant').textContent = s.dormant_candidate;
document.getElementById('c-archive').textContent = s.archive_legit;
}
function renderSuiteFilter() {
const suites = [...new Set(DATA.merged.map(m => m.suite_v82))].sort();
const sel = document.getElementById('suite-filter');
suites.forEach(s => sel.innerHTML += `<option value="${s}">${s}</option>`);
}
function render() {
let filtered = DATA.merged;
if (CURRENT_FILTER !== 'all') filtered = filtered.filter(m => m.classif_d91 === CURRENT_FILTER);
if (CURRENT_SUITE) filtered = filtered.filter(m => m.suite_v82 === CURRENT_SUITE);
if (SEARCH_TERM) {
const q = SEARCH_TERM.toLowerCase();
filtered = filtered.filter(m =>
m.page.toLowerCase().includes(q) ||
(m.title||'').toLowerCase().includes(q) ||
(m.suite_v82||'').toLowerCase().includes(q)
);
}
const container = document.getElementById('pages');
if (filtered.length === 0) {
container.innerHTML = '<div class="empty">Aucun orphelin pour ce filtre</div>';
return;
}
container.innerHTML = filtered.map(m => {
const cls = m.classif_d91.toLowerCase();
return `
<a href="${m.url}" class="page ${cls}" target="_blank">
<div class="page-header">
<div class="pn">${m.page}</div>
<span class="badge ${cls}">${m.classif_d91}</span>
</div>
<div class="pt">${m.title || '(sans titre)'}</div>
<div class="pm">
<span class="badge suite">${m.suite_v82}</span>
<span>${m.size_kb} KB</span>
<span>${m.class}</span>
${m.reason ? '<span>· '+m.reason+'</span>' : ''}
</div>
</a>
`;
}).join('');
}
document.querySelectorAll('.tab').forEach(t => t.onclick = () => {
document.querySelectorAll('.tab').forEach(x => x.classList.remove('active'));
t.classList.add('active');
CURRENT_FILTER = t.dataset.filter;
render();
});
document.getElementById('search').oninput = e => { SEARCH_TERM = e.target.value; render(); };
document.getElementById('suite-filter').onchange = e => { CURRENT_SUITE = e.target.value; render(); };
function showSnippet() {
document.getElementById('snippet-modal').classList.add('show');
document.getElementById('snippet-code').textContent = DATA.snippet_html || '(empty)';
}
function hideSnippet() { document.getElementById('snippet-modal').classList.remove('show'); }
function copySnippet() {
navigator.clipboard.writeText(DATA.snippet_html);
alert('Snippet copié — paste avant
<!-- === 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) {
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 (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);} });
}
}
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=1776776094" 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> dans WTP');
}
load();
</script>
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
</body></html>