620 lines
32 KiB
HTML
620 lines
32 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>WEVAL · ERP Launchpad — Next-gen UX (beyond S/4HANA Fiori)</title>
|
|
<style>
|
|
:root{
|
|
--bg-0:#05060a; --bg-1:#0b0d15; --bg-2:#11141f; --bg-3:#171b2a; --bg-tile:#0f1420; --bg-hover:#1a1f30;
|
|
--border:rgba(99,102,241,0.15); --border-h:rgba(99,102,241,0.38); --border-a:rgba(20,184,166,0.5);
|
|
--text:#e2e8f0; --dim:#94a3b8; --mute:#64748b;
|
|
--accent:#14b8a6; --accent2:#6366f1; --purple:#a855f7; --cyan:#06b6d4;
|
|
--ok:#22c55e; --warn:#f59e0b; --err:#ef4444; --gold:#eab308; --rose:#f43f5e;
|
|
/* Module category colors */
|
|
--c-intel:#ec4899; --c-fin:#3b82f6; --c-commerce:#10b981; --c-mkt:#f59e0b;
|
|
--c-growth:#06b6d4; --c-hr:#a855f7; --c-supply:#06b6d4; --c-ops:#14b8a6;
|
|
--c-erp:#f43f5e; --c-comm:#6366f1; --c-sec:#ef4444; --c-dev:#8b5cf6;
|
|
--c-know:#eab308; --c-multi:#ec4899; --c-rd:#22c55e; --c-l6s:#ff6b35;
|
|
}
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:'Inter',system-ui,sans-serif;background:radial-gradient(ellipse 1600px 900px at 30% -10%,#141a2e,#05060a 60%);color:var(--text);min-height:100vh;font-size:13px;line-height:1.5}
|
|
|
|
/* SHELL */
|
|
.shell{position:sticky;top:0;z-index:100;background:rgba(5,6,10,0.92);backdrop-filter:blur(14px);border-bottom:1px solid var(--border);padding:12px 22px;display:flex;align-items:center;gap:14px}
|
|
.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:15px;background:linear-gradient(90deg,#22d3ee,#a855f7,#eab308);-webkit-background-clip:text;background-clip:text;color:transparent}
|
|
.brand::before{content:'';display:inline-block;width:10px;height:10px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 0 rgba(34,197,94,.7);animation:pulse 2s infinite}
|
|
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,.7)}70%{box-shadow:0 0 0 10px rgba(34,197,94,0)}}
|
|
.searchbox{flex:1;max-width:620px;position:relative}
|
|
.searchbox input{width:100%;background:var(--bg-2);border:1px solid var(--border);color:var(--text);padding:10px 16px 10px 40px;border-radius:10px;font-size:13.5px;font-family:inherit;transition:all .15s}
|
|
.searchbox input:focus{outline:none;border-color:var(--border-a);box-shadow:0 0 0 3px rgba(20,184,166,0.12)}
|
|
.searchbox::before{content:'⌕';position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--dim);font-size:17px}
|
|
.searchbox::after{content:'⌘ K';position:absolute;right:12px;top:50%;transform:translateY(-50%);background:var(--bg-3);color:var(--mute);font-size:10px;padding:2px 7px;border-radius:4px;font-family:'JetBrains Mono',monospace}
|
|
.shell-right{display:flex;gap:6px;align-items:center}
|
|
.view-tabs{display:flex;gap:3px;background:var(--bg-2);padding:3px;border-radius:8px;border:1px solid var(--border)}
|
|
.vt{padding:5px 10px;background:transparent;border:none;color:var(--dim);font-size:11px;cursor:pointer;font-family:inherit;border-radius:5px;transition:all .15s}
|
|
.vt:hover{color:var(--text)}
|
|
.vt.active{background:var(--bg-3);color:var(--accent)}
|
|
.btn{padding:7px 12px;background:var(--bg-2);border:1px solid var(--border);color:var(--text);border-radius:8px;font-size:11.5px;cursor:pointer;text-decoration:none;font-family:inherit;transition:all .15s}
|
|
.btn:hover{border-color:var(--accent);color:var(--accent)}
|
|
|
|
/* CONTAINER */
|
|
.wrap{max-width:1760px;margin:0 auto;padding:20px 22px 100px}
|
|
|
|
/* QUICK LINKS Favoris */
|
|
.quick{background:linear-gradient(135deg,rgba(234,179,8,0.06),rgba(168,85,247,0.05));border:1px solid rgba(234,179,8,0.18);border-radius:14px;padding:14px 16px;margin-bottom:20px}
|
|
.quick-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
|
|
.quick-head .t{font-size:11px;letter-spacing:1.8px;text-transform:uppercase;color:var(--gold);font-weight:800}
|
|
.quick-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:8px}
|
|
.qt{background:var(--bg-2);border:1px solid var(--border);border-radius:10px;padding:10px 12px;cursor:pointer;transition:all .15s;text-decoration:none;color:var(--text);display:block;position:relative}
|
|
.qt:hover{border-color:var(--border-a);transform:translateY(-2px);background:var(--bg-hover)}
|
|
.qt .ic{font-size:22px;margin-bottom:5px;display:block}
|
|
.qt .n{font-size:11.5px;font-weight:700;line-height:1.2}
|
|
.qt .d{font-size:10px;color:var(--dim);margin-top:2px}
|
|
.qt-add{display:flex;align-items:center;justify-content:center;background:transparent;border:1.5px dashed var(--border);color:var(--mute);cursor:pointer;font-size:20px}
|
|
.qt-add:hover{border-color:var(--accent);color:var(--accent)}
|
|
|
|
/* Category nav rail */
|
|
.cat-rail{display:flex;gap:6px;overflow-x:auto;padding:4px 0;margin-bottom:16px;scrollbar-width:none}
|
|
.cat-rail::-webkit-scrollbar{display:none}
|
|
.cat-btn{flex-shrink:0;padding:7px 14px;background:var(--bg-2);border:1px solid var(--border);color:var(--dim);border-radius:20px;font-size:11.5px;cursor:pointer;font-family:inherit;transition:all .15s;display:flex;align-items:center;gap:6px}
|
|
.cat-btn:hover{color:var(--text);border-color:var(--border-h)}
|
|
.cat-btn.active{background:linear-gradient(135deg,var(--accent),var(--cyan));color:white;border:none;font-weight:700}
|
|
.cat-btn .cnt{font-size:10px;opacity:0.7;background:rgba(255,255,255,0.1);padding:1px 5px;border-radius:3px;margin-left:3px}
|
|
|
|
/* MODULE ACCORDION */
|
|
.module-sec{margin-bottom:18px}
|
|
.module-head{display:flex;align-items:center;gap:10px;padding:10px 14px;background:var(--bg-1);border:1px solid var(--border);border-radius:10px;cursor:pointer;transition:all .15s}
|
|
.module-head:hover{border-color:var(--border-h)}
|
|
.module-head .chev{font-size:10px;color:var(--dim);transition:transform .2s}
|
|
.module-head.open .chev{transform:rotate(90deg)}
|
|
.module-head .ic{font-size:22px}
|
|
.module-head .info{flex:1}
|
|
.module-head .name{font-size:14px;font-weight:700;letter-spacing:0.2px}
|
|
.module-head .sub{font-size:11px;color:var(--dim);margin-top:1px}
|
|
.module-head .cnt{background:var(--bg-3);color:var(--accent);padding:3px 10px;border-radius:10px;font-size:11px;font-family:'JetBrains Mono',monospace;font-weight:700}
|
|
|
|
/* SUB-MODULE GRID — S/4 killer */
|
|
.submod-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:10px;margin-top:10px;padding:4px;max-height:0;overflow:hidden;transition:max-height .3s ease}
|
|
.module-sec.open .submod-grid{max-height:4000px}
|
|
.submod{background:var(--bg-tile);border:1px solid var(--border);border-radius:12px;overflow:hidden;transition:all .18s;position:relative;cursor:pointer;display:flex;flex-direction:column}
|
|
.submod:hover{border-color:var(--border-a);transform:translateY(-3px);box-shadow:0 12px 35px rgba(0,0,0,0.4)}
|
|
.submod::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:var(--mc);opacity:0.8}
|
|
.submod-favorite{position:absolute;top:10px;right:10px;background:transparent;border:none;color:var(--mute);font-size:16px;cursor:pointer;transition:all .15s;z-index:2}
|
|
.submod-favorite:hover{color:var(--gold);transform:scale(1.15)}
|
|
.submod-favorite.on{color:var(--gold)}
|
|
.submod-body{padding:14px 14px 10px}
|
|
.submod-head{display:flex;align-items:flex-start;gap:10px;margin-bottom:8px;padding-right:26px}
|
|
.submod-ic{width:36px;height:36px;border-radius:8px;background:linear-gradient(135deg,rgba(20,184,166,0.15),rgba(99,102,241,0.12));display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}
|
|
.submod-name{font-size:13px;font-weight:700;color:var(--text);line-height:1.25;margin-bottom:3px}
|
|
.submod-desc{font-size:10.5px;color:var(--dim);line-height:1.4;min-height:2.6em;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
|
.submod-kpi{display:flex;gap:10px;margin-top:10px;padding:8px 0;border-top:1px dashed var(--bg-3);font-family:'JetBrains Mono',monospace}
|
|
.kpi-mini{flex:1;text-align:center}
|
|
.kpi-mini .v{font-size:15px;font-weight:800;color:var(--accent)}
|
|
.kpi-mini .l{font-size:9px;color:var(--mute);text-transform:uppercase;letter-spacing:0.3px}
|
|
.submod-pills{display:flex;flex-wrap:wrap;gap:3px;margin-top:8px}
|
|
.pill{font-size:9.5px;padding:2px 7px;border-radius:4px;font-family:'JetBrains Mono',monospace;font-weight:600;letter-spacing:0.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:140px}
|
|
.pill.page{background:rgba(34,197,94,0.12);color:#86efac}
|
|
.pill.api{background:rgba(245,158,11,0.12);color:#fbbf24}
|
|
.pill.script{background:rgba(168,85,247,0.12);color:#d4a7fa}
|
|
.pill.path{background:rgba(99,102,241,0.12);color:#a5b4fc}
|
|
.submod-foot{margin-top:auto;padding:8px 14px;background:rgba(0,0,0,0.3);border-top:1px solid rgba(255,255,255,0.04);display:flex;justify-content:space-between;align-items:center}
|
|
.submod-open{font-size:10.5px;color:var(--accent);font-weight:700;text-decoration:none}
|
|
.submod-open:hover{color:var(--cyan)}
|
|
.submod-status{font-size:9px;padding:2px 7px;border-radius:4px;font-weight:700;letter-spacing:0.3px;text-transform:uppercase}
|
|
.submod-status.live{background:rgba(34,197,94,0.18);color:#86efac}
|
|
|
|
/* COMPACT list view */
|
|
.compact-list{display:none;flex-direction:column;gap:4px}
|
|
.compact-list.active{display:flex}
|
|
.compact-item{display:grid;grid-template-columns:32px 1.5fr 2fr auto auto;gap:12px;align-items:center;padding:10px 14px;background:var(--bg-1);border:1px solid var(--border);border-radius:8px;cursor:pointer;transition:all .1s}
|
|
.compact-item:hover{background:var(--bg-hover);border-color:var(--border-h)}
|
|
.compact-item .c-ic{font-size:18px}
|
|
.compact-item .c-n{font-size:12.5px;font-weight:700}
|
|
.compact-item .c-d{font-size:11px;color:var(--dim)}
|
|
.compact-item .c-m{font-size:10px;color:var(--mute);font-family:'JetBrains Mono',monospace}
|
|
.compact-item .c-a{color:var(--accent);font-size:13px}
|
|
|
|
/* Command K overlay */
|
|
.cmdk{position:fixed;inset:0;background:rgba(0,0,0,0.7);backdrop-filter:blur(8px);display:none;z-index:200;align-items:flex-start;justify-content:center;padding-top:90px}
|
|
.cmdk.show{display:flex}
|
|
.cmdk-box{background:var(--bg-1);border:1px solid var(--border-h);border-radius:14px;width:90%;max-width:640px;max-height:70vh;overflow:hidden;box-shadow:0 20px 80px rgba(0,0,0,0.5);display:flex;flex-direction:column}
|
|
.cmdk-input{width:100%;background:transparent;border:none;color:var(--text);padding:18px 22px;font-size:16px;border-bottom:1px solid var(--border);font-family:inherit}
|
|
.cmdk-input:focus{outline:none}
|
|
.cmdk-results{flex:1;overflow-y:auto;padding:8px}
|
|
.cmdk-item{padding:10px 14px;border-radius:8px;cursor:pointer;display:flex;align-items:center;gap:12px;transition:all .1s}
|
|
.cmdk-item:hover,.cmdk-item.selected{background:var(--bg-3)}
|
|
.cmdk-item .icon{font-size:20px}
|
|
.cmdk-item .txt{flex:1}
|
|
.cmdk-item .t-n{font-size:13px;font-weight:600}
|
|
.cmdk-item .t-m{font-size:11px;color:var(--dim);margin-top:1px}
|
|
.cmdk-footer{padding:10px 20px;border-top:1px solid var(--border);font-size:10.5px;color:var(--mute);display:flex;gap:14px}
|
|
|
|
/* Clock */
|
|
.clock{font-family:'JetBrains Mono',monospace;color:var(--accent);font-size:10px}
|
|
|
|
/* Empty */
|
|
.empty{text-align:center;padding:60px;color:var(--dim)}
|
|
.spinner{width:32px;height:32px;border:3px solid var(--bg-3);border-top-color:var(--accent);border-radius:50%;margin:0 auto 12px;animation:spin 1s linear infinite}
|
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
|
|
/* Responsive */
|
|
@media(max-width:900px){
|
|
.searchbox{max-width:none}
|
|
.cmdk-box{width:96%;max-width:none}
|
|
.view-tabs{display:none}
|
|
}
|
|
|
|
/* === 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>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="shell">
|
|
<div class="brand">WEVAL · Launchpad</div>
|
|
<div class="searchbox">
|
|
<input id="search" placeholder="Rechercher page, API, agent, KPI, dept (Cmd-K pour recherche globale)" autocomplete="off">
|
|
</div>
|
|
<div class="shell-right">
|
|
<div class="view-tabs">
|
|
<button class="vt active" data-view="grid">▦ Grid</button>
|
|
<button class="vt" data-view="compact">☰ Compact</button>
|
|
</div>
|
|
<a href="/weval-technology-platform.html" class="btn">← WTP</a>
|
|
<a href="/enterprise-complete.html" class="btn">🏢 Enterprise</a>
|
|
<a href="/sales-hub.html" class="btn">🚀 Sales</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wrap">
|
|
|
|
<!-- QUICK / FAVORIS -->
|
|
<div class="quick">
|
|
<div class="quick-head">
|
|
<div class="t">⭐ Mes favoris · quick actions</div>
|
|
<span class="clock" id="clock">—</span>
|
|
</div>
|
|
<div class="quick-tiles" id="quick-tiles"></div>
|
|
</div>
|
|
|
|
<!-- CAT RAIL -->
|
|
<div class="cat-rail" id="cat-rail"></div>
|
|
|
|
<!-- MODULES -->
|
|
<div id="grid-view">
|
|
<div class="empty"><div class="spinner"></div>Chargement modules ERP…</div>
|
|
</div>
|
|
|
|
<!-- COMPACT view -->
|
|
<div class="compact-list" id="compact-view"></div>
|
|
|
|
</div>
|
|
|
|
<!-- CMD-K -->
|
|
<div class="cmdk" id="cmdk">
|
|
<div class="cmdk-box">
|
|
<input class="cmdk-input" id="cmdk-input" placeholder="Rechercher partout (pages, API, agents, docs, KPIs)…" autocomplete="off">
|
|
<div class="cmdk-results" id="cmdk-results"></div>
|
|
<div class="cmdk-footer">
|
|
<span>↑↓ naviguer</span>
|
|
<span>↵ ouvrir</span>
|
|
<span>esc fermer</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const WTP_API = '/api/weval-technology-platform-api.php';
|
|
|
|
// Catégorie meta (icon + color css var)
|
|
const CAT = {
|
|
intelligence: {ic:'🧠', c:'var(--c-intel)'},
|
|
commerce: {ic:'💼', c:'var(--c-commerce)'},
|
|
finance: {ic:'💰', c:'var(--c-fin)'},
|
|
marketing: {ic:'📣', c:'var(--c-mkt)'},
|
|
growth: {ic:'📈', c:'var(--c-growth)'},
|
|
hr: {ic:'👥', c:'var(--c-hr)'},
|
|
supply: {ic:'📦', c:'var(--c-supply)'},
|
|
operations: {ic:'⚙️', c:'var(--c-ops)'},
|
|
erp_integrations:{ic:'🔌', c:'var(--c-erp)'},
|
|
communications:{ic:'💬', c:'var(--c-comm)'},
|
|
security: {ic:'🔒', c:'var(--c-sec)'},
|
|
development: {ic:'🛠️', c:'var(--c-dev)'},
|
|
knowledge: {ic:'📚', c:'var(--c-know)'},
|
|
multimodal: {ic:'🎨', c:'var(--c-multi)'},
|
|
rd: {ic:'🔬', c:'var(--c-rd)'},
|
|
lean6sigma: {ic:'🎯', c:'var(--c-l6s)'}
|
|
};
|
|
|
|
// Default favorites
|
|
const DEFAULT_FAVS = [
|
|
{id:'sales_hub', url:'/sales-hub.html', ic:'🚀', n:'Sales Hub', d:'Cockpit commercial'},
|
|
{id:'pitch', url:'/pitch.html', ic:'📄', n:'Pitch 1 écran', d:'Landing prospect'},
|
|
{id:'dg_cc', url:'/dg-command-center.html', ic:'🎖', n:'DG Center', d:'Pilotage direction'},
|
|
{id:'ent_cmp', url:'/enterprise-complete.html', ic:'🏢', n:'Enterprise', d:'20 depts · 169 KPIs'},
|
|
{id:'roi_sim', url:'/agent-roi-simulator.html', ic:'🧮', n:'ROI Simulator', d:'Discovery live'},
|
|
{id:'atlas', url:'/pain-points-atlas.html', ic:'🗺️', n:'Atlas', d:'35 pain points'},
|
|
{id:'intel', url:'/intelligence-growth.html', ic:'🌐', n:'Intel&Growth', d:'DarkScout · opps'},
|
|
{id:'wevia', url:'/wevia-master.html', ic:'🤖', n:'WEVIA Master', d:'Chat multi-agents'}
|
|
];
|
|
|
|
let DATA = null;
|
|
let FAVS = JSON.parse(localStorage.getItem('weval-favs') || JSON.stringify(DEFAULT_FAVS));
|
|
let currentFilter = 'all';
|
|
let currentView = 'grid';
|
|
|
|
function clockTick(){document.getElementById('clock').textContent = new Date().toLocaleTimeString('fr-FR') + ' · auto-refresh 60s';}
|
|
setInterval(clockTick,1000); clockTick();
|
|
|
|
async function load(){
|
|
try {
|
|
const r = await fetch(WTP_API+'?t='+Date.now());
|
|
DATA = await r.json();
|
|
render();
|
|
} catch(e) { console.error(e); }
|
|
}
|
|
|
|
function render(){
|
|
if(!DATA) return;
|
|
renderFavs();
|
|
renderCatRail();
|
|
renderGrid();
|
|
renderCompact();
|
|
}
|
|
|
|
function renderFavs(){
|
|
document.getElementById('quick-tiles').innerHTML = FAVS.map(f => `
|
|
<a class="qt" href="${f.url}" target="_blank" title="${f.d}">
|
|
<span class="ic">${f.ic}</span>
|
|
<div class="n">${f.n}</div>
|
|
<div class="d">${f.d}</div>
|
|
</a>
|
|
`).join('') + `<button class="qt qt-add" onclick="addFavorite()" title="Ajouter un favori">+</button>`;
|
|
}
|
|
|
|
function renderCatRail(){
|
|
const modules = DATA.modules || {};
|
|
const total = Object.values(modules).reduce((s,m)=>s+(m.submodules||[]).length, 0);
|
|
let html = `<button class="cat-btn ${currentFilter==='all'?'active':''}" onclick="setFilter('all')">Tous <span class="cnt">${total}</span></button>`;
|
|
for (const [mid, m] of Object.entries(modules)) {
|
|
const ic = (CAT[mid]||{}).ic || m.icon || '📁';
|
|
const n = m.label || mid;
|
|
const c = (m.submodules||[]).length;
|
|
html += `<button class="cat-btn ${currentFilter===mid?'active':''}" onclick="setFilter('${mid}')">${ic} ${n} <span class="cnt">${c}</span></button>`;
|
|
}
|
|
document.getElementById('cat-rail').innerHTML = html;
|
|
}
|
|
|
|
function setFilter(mid){
|
|
currentFilter = mid;
|
|
renderCatRail();
|
|
renderGrid();
|
|
renderCompact();
|
|
}
|
|
|
|
function renderGrid(){
|
|
if (currentView !== 'grid') { document.getElementById('grid-view').style.display='none'; return; }
|
|
document.getElementById('grid-view').style.display='block';
|
|
document.getElementById('compact-view').classList.remove('active');
|
|
|
|
const modules = DATA.modules || {};
|
|
let entries = Object.entries(modules);
|
|
if (currentFilter !== 'all') entries = entries.filter(([k]) => k === currentFilter);
|
|
|
|
let html = '';
|
|
for (const [mid, m] of entries) {
|
|
const ic = (CAT[mid]||{}).ic || m.icon || '📁';
|
|
const mc = (CAT[mid]||{}).c || 'var(--accent)';
|
|
const subs = m.submodules || [];
|
|
const open = currentFilter === mid || entries.length <= 4 ? 'open' : '';
|
|
|
|
html += `<div class="module-sec ${open}" data-mid="${mid}">
|
|
<div class="module-head ${open}" onclick="toggleModule('${mid}')">
|
|
<span class="chev">▸</span>
|
|
<span class="ic">${ic}</span>
|
|
<div class="info">
|
|
<div class="name">${m.label || mid}</div>
|
|
<div class="sub">${m.desc||m.description||''}</div>
|
|
</div>
|
|
<span class="cnt">${subs.length} sous-modules</span>
|
|
</div>
|
|
<div class="submod-grid" style="--mc:${mc}">
|
|
${subs.map(s => renderSubModule(s, mc, mid)).join('')}
|
|
</div>
|
|
</div>`;
|
|
}
|
|
document.getElementById('grid-view').innerHTML = html;
|
|
}
|
|
|
|
function renderSubModule(s, mc, mid) {
|
|
const pages = (s.pages||[]).slice(0,3);
|
|
const apis = (s.apis||[]).slice(0,3);
|
|
const scripts = (s.scripts||[]).slice(0,2);
|
|
const firstUrl = (s.pages||[])[0] || (s.apis||[])[0] || s.url || '#';
|
|
const openUrl = firstUrl && !firstUrl.startsWith('/') ? '/'+firstUrl : firstUrl;
|
|
const fav = FAVS.find(f=>f.id===s.id) ? 'on' : '';
|
|
const icMap = {crm:'🤝',sales:'💼',pipeline:'📊',candidates:'👥',agents:'🤖',roi:'🧮',atlas:'🗺️',offer:'💰',chatbot:'💬',pitch:'📄',hub:'🚀',board:'📋',market:'📈',insights:'🔍',growth:'🌱',finance:'💳',procure:'🛒',manuf:'🏭',quality:'✅',payroll:'💵',security:'🔒',legal:'⚖️'};
|
|
const sub_ic = s.icon || (Object.entries(icMap).find(([k])=>s.id.includes(k))||[,'📦'])[1];
|
|
const pagesCount = (s.pages||[]).length;
|
|
const apisCount = (s.apis||[]).length;
|
|
|
|
return `<div class="submod" style="--mc:${mc}" onclick="openSubModule('${openUrl}', event)">
|
|
<button class="submod-favorite ${fav}" onclick="event.stopPropagation();toggleFav('${s.id}', ${JSON.stringify({ic:sub_ic, n:s.label||s.id, d:(s.desc||'').substring(0,40), url:openUrl}).replace(/"/g,'"')})">${fav?'★':'☆'}</button>
|
|
<div class="submod-body">
|
|
<div class="submod-head">
|
|
<div class="submod-ic">${sub_ic}</div>
|
|
<div>
|
|
<div class="submod-name">${s.label || s.id}</div>
|
|
<div class="submod-desc">${s.desc || s.description || ''}</div>
|
|
</div>
|
|
</div>
|
|
<div class="submod-kpi">
|
|
<div class="kpi-mini"><div class="v">${pagesCount}</div><div class="l">pages</div></div>
|
|
<div class="kpi-mini"><div class="v">${apisCount}</div><div class="l">apis</div></div>
|
|
<div class="kpi-mini"><div class="v">${scripts.length}</div><div class="l">scripts</div></div>
|
|
</div>
|
|
<div class="submod-pills">
|
|
${pages.map(p=>`<span class="pill page" title="${p}">${p.length>18?p.substring(0,16)+'…':p}</span>`).join('')}
|
|
${apis.slice(0,2).map(a=>`<span class="pill api" title="${a}">${(a.split('/').pop()||a).substring(0,18)}</span>`).join('')}
|
|
</div>
|
|
</div>
|
|
<div class="submod-foot">
|
|
<a class="submod-open" href="${openUrl}" target="_blank" onclick="event.stopPropagation()">→ Ouvrir</a>
|
|
<span class="submod-status live">live</span>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
function renderCompact(){
|
|
if (currentView !== 'compact') { document.getElementById('compact-view').classList.remove('active'); return; }
|
|
document.getElementById('grid-view').style.display='none';
|
|
document.getElementById('compact-view').classList.add('active');
|
|
|
|
const modules = DATA.modules || {};
|
|
let entries = Object.entries(modules);
|
|
if (currentFilter !== 'all') entries = entries.filter(([k]) => k === currentFilter);
|
|
|
|
let html = '';
|
|
for (const [mid, m] of entries) {
|
|
for (const s of (m.submodules||[])) {
|
|
const ic = s.icon || '📦';
|
|
const firstUrl = (s.pages||[])[0] || (s.apis||[])[0] || '#';
|
|
const openUrl = firstUrl && !firstUrl.startsWith('/') ? '/'+firstUrl : firstUrl;
|
|
html += `<div class="compact-item" onclick="window.open('${openUrl}','_blank')">
|
|
<div class="c-ic">${ic}</div>
|
|
<div class="c-n">${s.label || s.id}</div>
|
|
<div class="c-d">${(s.desc||'').substring(0,80)}</div>
|
|
<div class="c-m">${(s.pages||[]).length}p · ${(s.apis||[]).length}a</div>
|
|
<div class="c-a">→</div>
|
|
</div>`;
|
|
}
|
|
}
|
|
document.getElementById('compact-view').innerHTML = html || '<div class="empty">Aucun résultat</div>';
|
|
}
|
|
|
|
function toggleModule(mid){
|
|
document.querySelectorAll(`.module-sec[data-mid="${mid}"]`).forEach(el => el.classList.toggle('open'));
|
|
document.querySelectorAll(`.module-sec[data-mid="${mid}"] .module-head`).forEach(el => el.classList.toggle('open'));
|
|
}
|
|
|
|
function openSubModule(url, e){ if (e.target.closest('.submod-favorite')) return; if (url && url !== '#') window.open(url, '_blank'); }
|
|
|
|
function toggleFav(id, meta){
|
|
const idx = FAVS.findIndex(f=>f.id===id);
|
|
if (idx >= 0) FAVS.splice(idx, 1);
|
|
else {
|
|
try {
|
|
const m = typeof meta === 'string' ? JSON.parse(meta.replace(/"/g,'"')) : meta;
|
|
FAVS.push({id, ...m});
|
|
} catch(e){ console.error(e); }
|
|
}
|
|
localStorage.setItem('weval-favs', JSON.stringify(FAVS));
|
|
renderFavs();
|
|
renderGrid();
|
|
}
|
|
|
|
function addFavorite(){
|
|
document.getElementById('cmdk').classList.add('show');
|
|
document.getElementById('cmdk-input').focus();
|
|
}
|
|
|
|
// View switcher
|
|
document.querySelectorAll('.vt').forEach(b => b.onclick = () => {
|
|
document.querySelectorAll('.vt').forEach(x => x.classList.toggle('active', x===b));
|
|
currentView = b.dataset.view;
|
|
renderGrid(); renderCompact();
|
|
});
|
|
|
|
// Search top
|
|
document.getElementById('search').addEventListener('input', debounce(e => {
|
|
const q = e.target.value.toLowerCase().trim();
|
|
if (!q) { renderGrid(); renderCompact(); return; }
|
|
// Filter on submods only
|
|
const modules = DATA.modules || {};
|
|
let html = '';
|
|
for (const [mid, m] of Object.entries(modules)) {
|
|
const mc = (CAT[mid]||{}).c || 'var(--accent)';
|
|
const matches = (m.submodules||[]).filter(s => {
|
|
const hay = [s.label||'', s.desc||'', s.id||'', ...(s.pages||[]), ...(s.apis||[]), ...(s.scripts||[])].join(' ').toLowerCase();
|
|
return hay.includes(q);
|
|
});
|
|
if (matches.length === 0) continue;
|
|
html += `<div class="module-sec open" data-mid="${mid}">
|
|
<div class="module-head open"><span class="chev">▸</span><span class="ic">${(CAT[mid]||{}).ic||'📁'}</span>
|
|
<div class="info"><div class="name">${m.label}</div><div class="sub">${matches.length} match</div></div><span class="cnt">${matches.length}</span></div>
|
|
<div class="submod-grid" style="--mc:${mc}">${matches.map(s=>renderSubModule(s, mc, mid)).join('')}</div>
|
|
</div>`;
|
|
}
|
|
document.getElementById('grid-view').innerHTML = html || '<div class="empty">Aucun résultat pour « '+q+' »</div>';
|
|
}, 250));
|
|
|
|
function debounce(fn, t){let to; return (...a)=>{clearTimeout(to);to=setTimeout(()=>fn(...a),t)};}
|
|
|
|
// CMD-K
|
|
window.addEventListener('keydown', e => {
|
|
if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase()==='k') {
|
|
e.preventDefault();
|
|
document.getElementById('cmdk').classList.add('show');
|
|
document.getElementById('cmdk-input').focus();
|
|
}
|
|
if (e.key === 'Escape') document.getElementById('cmdk').classList.remove('show');
|
|
});
|
|
document.getElementById('cmdk').addEventListener('click', e => { if (e.target.id==='cmdk') e.currentTarget.classList.remove('show'); });
|
|
document.getElementById('cmdk-input').addEventListener('input', e => {
|
|
const q = e.target.value.toLowerCase().trim();
|
|
const modules = DATA?.modules || {};
|
|
const items = [];
|
|
for (const [mid, m] of Object.entries(modules)) {
|
|
for (const s of (m.submodules||[])) {
|
|
const hay = [s.label||'', s.desc||'', s.id||'', ...(s.pages||[]), ...(s.apis||[])].join(' ').toLowerCase();
|
|
if (q === '' || hay.includes(q)) items.push({s, mid, m});
|
|
if (items.length >= 25) break;
|
|
}
|
|
if (items.length >= 25) break;
|
|
}
|
|
document.getElementById('cmdk-results').innerHTML = items.map(({s, mid, m}) => {
|
|
const url = (s.pages||[])[0] || (s.apis||[])[0] || '#';
|
|
const openUrl = url && !url.startsWith('/') ? '/'+url : url;
|
|
return `<div class="cmdk-item" onclick="window.open('${openUrl}','_blank');document.getElementById('cmdk').classList.remove('show')">
|
|
<span class="icon">${s.icon || (CAT[mid]||{}).ic || '📦'}</span>
|
|
<div class="txt"><div class="t-n">${s.label||s.id}</div><div class="t-m">${m.label} · ${openUrl}</div></div>
|
|
</div>`;
|
|
}).join('') || '<div class="empty" style="padding:30px">Aucun résultat</div>';
|
|
});
|
|
|
|
load();
|
|
setInterval(load, 60000);
|
|
</script>
|
|
<script>
|
|
/* V75 AVATAR UNIFIER — Meeting-rooms emoji style (Opus 19avr) */
|
|
(function() {
|
|
if (window.__WEVAL_AVATAR_V75) return;
|
|
window.__WEVAL_AVATAR_V75 = true;
|
|
const REG_URL = '/api/agent-avatars-v75.json';
|
|
const SVG_EP = '/api/agent-avatar-svg.php';
|
|
function emojiSVGUrl(name, emoji) {
|
|
return SVG_EP + '?n=' + encodeURIComponent(name) + '&e=' + encodeURIComponent(emoji);
|
|
}
|
|
fetch(REG_URL + '?t=' + Date.now()).then(r => r.json()).then(REG => {
|
|
function getAvatarUrl(name) {
|
|
const rec = REG[name];
|
|
if (!rec) return null;
|
|
if (typeof rec === 'object' && rec.svg) return rec.svg;
|
|
if (typeof rec === 'object' && rec.emoji) return emojiSVGUrl(name, rec.emoji);
|
|
return typeof rec === 'string' ? rec : null;
|
|
}
|
|
function findCI(key) {
|
|
const lower = key.toLowerCase();
|
|
for (const k of Object.keys(REG)) if (k.toLowerCase() === lower) return k;
|
|
return null;
|
|
}
|
|
function apply() {
|
|
document.querySelectorAll('img').forEach(img => {
|
|
const key = img.alt || img.dataset.agent || img.dataset.name || img.title || '';
|
|
if (!key) return;
|
|
let url = getAvatarUrl(key);
|
|
if (!url) { const alt = findCI(key); if (alt) url = getAvatarUrl(alt); }
|
|
if (url && img.src !== url && !img.src.endsWith(url)) {
|
|
img.src = url;
|
|
img.setAttribute('data-weval-v75', '1');
|
|
}
|
|
});
|
|
document.querySelectorAll('[data-agent]:not([data-weval-v75-applied])').forEach(el => {
|
|
const name = el.dataset.agent;
|
|
const url = getAvatarUrl(name);
|
|
if (!url) return;
|
|
const img = document.createElement('img');
|
|
img.src = url; img.alt = name; img.title = name;
|
|
img.className = 'v75-avatar';
|
|
img.style.cssText = 'width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle;background:transparent';
|
|
el.setAttribute('data-weval-v75-applied', '1');
|
|
el.prepend(img);
|
|
});
|
|
}
|
|
apply();
|
|
setTimeout(apply, 400); setTimeout(apply, 1200); setTimeout(apply, 3000);
|
|
const mo = new MutationObserver(() => apply());
|
|
mo.observe(document.body, {childList: true, subtree: true});
|
|
setTimeout(() => mo.disconnect(), 20000);
|
|
console.log('[V75 AvatarUnifier] applied from', Object.keys(REG).length, 'agents');
|
|
}).catch(e => console.warn('[V75] fetch failed', e));
|
|
})();
|
|
</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) {
|
|
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 === -->
|
|
|
|
</body>
|
|
</html>
|