568 lines
36 KiB
HTML
568 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>WEVAL · Pain Points Atlas — Tous ERPs × Agents Autonomes × Savings €</title>
|
||
<style>
|
||
:root {
|
||
--bg-0:#05060a; --bg-1:#0b0d15; --bg-2:#11141f; --bg-3:#171b2a;
|
||
--border:rgba(99,102,241,0.15); --border-h:rgba(99,102,241,0.35);
|
||
--text:#e2e8f0; --dim:#94a3b8; --mute:#64748b;
|
||
--accent:#14b8a6; --accent2:#6366f1; --purple:#a855f7; --cyan:#06b6d4;
|
||
--ok:#22c55e; --warn:#f59e0b; --err:#ef4444; --rose:#f43f5e; --amber:#f59e0b;
|
||
--gold:#eab308;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: 'Inter', system-ui, sans-serif;
|
||
background: radial-gradient(ellipse at top, #0f1420, #05060a 65%);
|
||
color: var(--text); min-height: 100vh; font-size: 13.5px; line-height: 1.55;
|
||
}
|
||
.container { max-width: 1680px; margin: 0 auto; padding: 30px 36px 80px; }
|
||
|
||
/* HEADER */
|
||
header {
|
||
display: flex; justify-content: space-between; align-items: flex-start;
|
||
margin-bottom: 26px; padding-bottom: 20px; border-bottom: 1px solid var(--border);
|
||
}
|
||
header h1 {
|
||
font-size: 28px; font-weight: 800;
|
||
background: linear-gradient(90deg, #22d3ee, #a855f7, #eab308);
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
header .sub { color: var(--dim); font-size: 13.5px; margin-top: 7px; max-width: 850px; }
|
||
header .pitch {
|
||
margin-top: 12px; padding: 11px 16px;
|
||
background: linear-gradient(135deg, rgba(234,179,8,0.08), rgba(168,85,247,0.06));
|
||
border-left: 3px solid var(--gold);
|
||
border-radius: 6px; font-size: 13px; color: var(--text); line-height: 1.55;
|
||
}
|
||
.actions { display: flex; gap: 9px; }
|
||
.btn { padding: 8px 15px; background: var(--bg-2); border: 1px solid var(--border); color: var(--text); border-radius: 8px; font-size: 12.5px; cursor: pointer; text-decoration: none; font-family: inherit; transition: all .2s; display: inline-flex; gap: 6px; align-items: center; }
|
||
.btn:hover { border-color: var(--accent); color: var(--accent); }
|
||
.btn-pri { background: linear-gradient(135deg, var(--gold), var(--amber)); border: none; color: #0b0d15; font-weight: 700; }
|
||
.pulse { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 0 rgba(34,197,94,.7); animation: pulse 2s infinite; margin-right: 4px; }
|
||
@keyframes pulse { 0%{box-shadow:0 0 0 0 rgba(34,197,94,.7)} 70%{box-shadow:0 0 0 8px rgba(34,197,94,0)} 100%{box-shadow:0 0 0 0 rgba(34,197,94,0)} }
|
||
|
||
/* KPI STRIP */
|
||
.kpi-strip { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; margin-bottom: 28px; }
|
||
.kpi { background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; padding: 18px; position: relative; overflow: hidden; transition: all .2s; }
|
||
.kpi:hover { border-color: var(--accent); transform: translateY(-2px); }
|
||
.kpi::before { content: ''; position: absolute; left: 0; top: 0; width: 3px; height: 100%; background: var(--accent); }
|
||
.kpi.c2::before { background: var(--purple); }
|
||
.kpi.c3::before { background: var(--cyan); }
|
||
.kpi.c4::before { background: var(--rose); }
|
||
.kpi.gold::before { background: linear-gradient(180deg, var(--gold), var(--amber)); }
|
||
.kpi .lbl { color: var(--dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: .7px; margin-bottom: 6px; font-weight: 600; }
|
||
.kpi .val { font-size: 28px; font-weight: 800; color: var(--text); line-height: 1; letter-spacing: -0.3px; }
|
||
.kpi .val.gold { background: linear-gradient(135deg, var(--gold), var(--amber)); -webkit-background-clip: text; background-clip: text; color: transparent; }
|
||
.kpi .u { font-size: 13px; color: var(--dim); font-weight: 500; margin-left: 3px; }
|
||
.kpi .sub { color: var(--dim); font-size: 10.5px; margin-top: 5px; }
|
||
|
||
/* SECTION TITLE */
|
||
.st { font-size: 17px; font-weight: 700; color: var(--text); margin: 36px 0 14px; display: flex; align-items: center; gap: 10px; letter-spacing: -0.2px; }
|
||
.st::before { content: ''; width: 4px; height: 20px; background: linear-gradient(180deg, var(--accent), var(--purple)); border-radius: 2px; }
|
||
.st .suf { color: var(--dim); font-size: 12.5px; font-weight: 500; }
|
||
|
||
/* ERP GRID */
|
||
.erp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; }
|
||
.erp-card { background: var(--bg-1); border: 1px solid var(--border); border-radius: 10px; padding: 12px; cursor: default; transition: all .2s; position: relative; }
|
||
.erp-card:hover { border-color: var(--accent); transform: translateY(-2px); }
|
||
.erp-card::before { content: ''; position: absolute; left: 0; top: 0; width: 3px; height: 100%; border-radius: 10px 0 0 10px; background: var(--tier-color, var(--accent)); }
|
||
.erp-card .tier { font-size: 9px; padding: 1px 6px; border-radius: 8px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.3px; display: inline-block; margin-bottom: 5px; }
|
||
.erp-card .tier.enterprise { background: rgba(239,68,68,0.15); color: #fca5a5; }
|
||
.erp-card .tier.mid-market { background: rgba(245,158,11,0.15); color: #fbbf24; }
|
||
.erp-card .tier.smb { background: rgba(99,102,241,0.15); color: #a5b4fc; }
|
||
.erp-card .tier.niche { background: rgba(168,85,247,0.15); color: #d4a7fa; }
|
||
.erp-card .tier.open-source { background: rgba(34,197,94,0.15); color: #86efac; }
|
||
.erp-card .name { font-size: 12px; font-weight: 600; color: var(--text); }
|
||
.erp-card .count { font-size: 10.5px; color: var(--accent); margin-top: 4px; font-family: 'JetBrains Mono',monospace; }
|
||
|
||
/* FILTER BAR */
|
||
.filter-bar { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
|
||
.filter-pill { padding: 7px 12px; background: var(--bg-2); border: 1px solid var(--border); color: var(--dim); border-radius: 18px; font-size: 11.5px; cursor: pointer; font-family: inherit; transition: all .2s; }
|
||
.filter-pill:hover { color: var(--text); border-color: var(--accent); }
|
||
.filter-pill.active { background: linear-gradient(135deg, var(--accent2), var(--purple)); color: white; border: none; font-weight: 600; }
|
||
|
||
/* PAIN POINT CARDS */
|
||
.pp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); gap: 14px; }
|
||
.pp-card { background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px; padding: 18px; position: relative; overflow: hidden; transition: all .2s; }
|
||
.pp-card:hover { border-color: var(--border-h); }
|
||
.pp-card::before { content: ''; position: absolute; left: 0; top: 0; width: 4px; height: 100%; background: linear-gradient(180deg, var(--err), var(--amber)); }
|
||
.pp-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; margin-bottom: 10px; }
|
||
.pp-id { font-family: 'JetBrains Mono',monospace; color: var(--mute); font-size: 10.5px; font-weight: 600; }
|
||
.pp-dept-tag { font-size: 9.5px; padding: 2px 7px; border-radius: 8px; background: rgba(99,102,241,0.15); color: #a5b4fc; font-weight: 600; letter-spacing: 0.3px; text-transform: uppercase; }
|
||
.pp-pain { font-size: 13.5px; color: var(--text); font-weight: 600; line-height: 1.35; margin-bottom: 7px; display: flex; gap: 6px; }
|
||
.pp-pain::before { content: '🔴'; flex-shrink: 0; margin-top: 1px; }
|
||
.pp-biz { font-size: 11.5px; color: var(--dim); margin-bottom: 12px; line-height: 1.45; padding-left: 20px; }
|
||
.pp-why { font-size: 10.5px; color: var(--mute); margin-bottom: 10px; font-style: italic; padding: 8px 10px; background: var(--bg-2); border-radius: 6px; }
|
||
.pp-why strong { color: var(--err); display: block; margin-bottom: 2px; font-style: normal; font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
|
||
.pp-arrow { text-align: center; color: var(--accent); font-size: 18px; margin: 4px 0 6px; }
|
||
.pp-agent-box { padding: 10px 12px; background: linear-gradient(135deg, rgba(20,184,166,0.08), rgba(99,102,241,0.05)); border: 1px solid rgba(20,184,166,0.25); border-radius: 8px; margin-bottom: 10px; }
|
||
.pp-agent-label { font-size: 9.5px; color: var(--accent); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 3px; }
|
||
.pp-agent-name { font-size: 12.5px; color: var(--text); font-weight: 700; display: flex; gap: 5px; align-items: center; }
|
||
.pp-agent-name::before { content: '🤖'; }
|
||
.pp-agent-how { font-size: 10.5px; color: var(--dim); margin-top: 5px; line-height: 1.45; }
|
||
.pp-savings { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; background: linear-gradient(135deg, rgba(234,179,8,0.08), rgba(168,85,247,0.04)); border: 1px solid rgba(234,179,8,0.25); border-radius: 8px; }
|
||
.pp-sav-lbl { font-size: 10px; color: var(--gold); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }
|
||
.pp-sav-lbl::before { content: '💰 '; }
|
||
.pp-sav-val { font-size: 16px; font-weight: 800; background: linear-gradient(135deg, var(--gold), var(--amber)); -webkit-background-clip: text; background-clip: text; color: transparent; font-family: 'JetBrains Mono',monospace; }
|
||
.pp-sav-note { font-size: 9.5px; color: var(--dim); margin-top: 3px; grid-column: 2; text-align: right; }
|
||
.pp-erps { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); }
|
||
.pp-erps span { font-size: 9px; padding: 2px 6px; background: var(--bg-2); border-radius: 4px; color: var(--mute); font-family: 'JetBrains Mono',monospace; }
|
||
|
||
/* FRAMEWORK SHOWCASE */
|
||
.framework-card { background: linear-gradient(135deg, rgba(20,184,166,0.06), rgba(168,85,247,0.06)); border: 1px solid var(--border); border-radius: 14px; padding: 24px; margin-bottom: 18px; }
|
||
.framework-card h3 { font-size: 18px; color: var(--text); margin-bottom: 6px; }
|
||
.framework-card .desc { color: var(--dim); font-size: 13px; margin-bottom: 20px; }
|
||
.framework-steps { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
|
||
@media(max-width: 1200px) { .framework-steps { grid-template-columns: repeat(3, 1fr); } }
|
||
@media(max-width: 640px) { .framework-steps { grid-template-columns: repeat(2, 1fr); } }
|
||
.fw-step { background: var(--bg-1); padding: 14px; border-radius: 10px; text-align: center; border: 1px solid var(--border); position: relative; }
|
||
.fw-step-num { position: absolute; top: -10px; left: 10px; width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), var(--purple)); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 800; color: white; }
|
||
.fw-step-text { font-size: 11.5px; color: var(--text); line-height: 1.4; margin-top: 6px; }
|
||
|
||
/* DOGFOOD */
|
||
.dogfood { background: linear-gradient(135deg, rgba(234,179,8,0.10), rgba(168,85,247,0.06)); border: 1px solid rgba(234,179,8,0.25); border-radius: 14px; padding: 22px; display: grid; grid-template-columns: 80px 1fr auto; gap: 20px; align-items: center; }
|
||
.dogfood .icon { font-size: 50px; }
|
||
.dogfood h3 { font-size: 16px; color: var(--text); margin-bottom: 5px; }
|
||
.dogfood p { color: var(--dim); font-size: 12.5px; line-height: 1.55; }
|
||
.dogfood .stat { text-align: right; }
|
||
.dogfood .stat-v { font-size: 26px; font-weight: 800; color: var(--gold); font-family: 'JetBrains Mono',monospace; }
|
||
.dogfood .stat-l { font-size: 10.5px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.5px; }
|
||
|
||
@media(max-width: 1024px) {
|
||
.kpi-strip { grid-template-columns: repeat(2, 1fr); }
|
||
header { flex-direction: column; gap: 14px; }
|
||
.dogfood { grid-template-columns: 1fr; text-align: center; }
|
||
.dogfood .stat { text-align: center; }
|
||
}
|
||
|
||
.loading { text-align: center; padding: 60px; color: var(--dim); }
|
||
.spinner { width: 42px; height: 42px; border: 3px solid var(--bg-3); border-top-color: var(--accent); border-radius: 50%; margin: 0 auto 18px; animation: spin 1s linear infinite; }
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
|
||
/* === OPUS RESPONSIVE FIX v2 19avr — append-only, doctrine #14 === */
|
||
@media(max-width: 480px) {
|
||
html, body { overflow-x: hidden; max-width: 100vw; }
|
||
.container { padding: 16px 12px 40px; max-width: 100vw; }
|
||
header, header > * { width: 100%; max-width: 100%; }
|
||
header h1 { font-size: 22px; word-break: break-word; }
|
||
header .sub { font-size: 12.5px; word-break: break-word; max-width: 100%; }
|
||
.pitch, header .pitch { word-break: break-word; overflow-wrap: anywhere; font-size: 12px; }
|
||
.kpi-strip { grid-template-columns: 1fr; gap: 10px; }
|
||
.erp-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
||
.pp-grid { grid-template-columns: 1fr; gap: 12px; }
|
||
.filter-bar { gap: 4px; flex-wrap: wrap; }
|
||
.dogfood { padding: 16px; grid-template-columns: 1fr; }
|
||
.dogfood .icon { font-size: 36px; }
|
||
.dogfood .stat-v { font-size: 20px; }
|
||
.actions { flex-wrap: wrap; width: 100%; }
|
||
.actions .btn { font-size: 11.5px; padding: 6px 10px; }
|
||
.pp-card, .erp-card { max-width: 100%; word-break: break-word; }
|
||
pre, code { word-break: break-all; white-space: pre-wrap; }
|
||
}
|
||
@media(max-width: 360px) {
|
||
.erp-grid { grid-template-columns: 1fr; }
|
||
}
|
||
/* === OPUS RESPONSIVE FIX v2 END === */
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<header>
|
||
<div>
|
||
<h1>🗺️ Pain Points Atlas <span class="pulse"></span></h1>
|
||
<div class="sub">Tous les ERPs du marché · Pain points business catalogués · Agents autonomes WEVAL · Savings €/an quantifiés</div>
|
||
<div class="pitch" id="pitch-t">Chargement du pitch…</div>
|
||
</div>
|
||
<div class="actions">
|
||
<a href="/erp-gap-fill-offer.html" class="btn">← Offre V65</a>
|
||
<a href="/weval-technology-platform.html" class="btn">🏠 WTP</a>
|
||
<button class="btn btn-pri" onclick="window.print()">📑 Export</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- KPI STRIP -->
|
||
<div class="kpi-strip" id="kpi-strip">
|
||
<div class="kpi"><div class="lbl">ERPs couverts</div><div class="val" id="k-erps">—</div><div class="sub">S/4HANA · Oracle · Sage · Odoo · D365 · NetSuite · Workday · SF · Infor · IFS · Epicor…</div></div>
|
||
<div class="kpi c2"><div class="lbl">Pain points catalogués</div><div class="val" id="k-pp">—</div><div class="sub">9 départements métier</div></div>
|
||
<div class="kpi c3"><div class="lbl">Agents WEVAL uniques</div><div class="val" id="k-ag">—</div><div class="sub">1 pain = 1 agent autonome</div></div>
|
||
<div class="kpi c4"><div class="lbl">Savings moyen/agent</div><div class="val" id="k-avg">—<span class="u">€/an</span></div><div class="sub">ROI mesurable</div></div>
|
||
<div class="kpi gold"><div class="lbl">Savings total/client</div><div class="val gold" id="k-tot">—<span class="u">M€/an</span></div><div class="sub" id="k-tot-sub">potentiel maximal pack complet</div></div>
|
||
</div>
|
||
|
||
<!-- ERPs COVERED -->
|
||
<div class="st">📦 25 ERPs du marché couverts <span class="suf">— chaque ERP a ses gaps, nos agents les comblent</span></div>
|
||
<div class="erp-grid" id="erp-grid"><div class="loading"><div class="spinner"></div></div></div>
|
||
|
||
<!-- FILTER BAR -->
|
||
<div class="st">🔴 Pain Points → 🤖 Agents → 💰 Savings <span class="suf">— filtrer par département</span></div>
|
||
<div class="filter-bar" id="filter-bar"></div>
|
||
|
||
<!-- PAIN POINTS -->
|
||
<div class="pp-grid" id="pp-grid"><div class="loading"><div class="spinner"></div></div></div>
|
||
|
||
<!-- FRAMEWORK -->
|
||
<div class="st">🎯 Framework WEVAL <span class="suf">— méthodologie éprouvée, adaptable par client</span></div>
|
||
<div class="framework-card">
|
||
<h3 id="fw-title">WEVAL Gap-Fill Framework</h3>
|
||
<div class="desc" id="fw-desc">Pain Point Business → Agent Autonome → Savings €/an</div>
|
||
<div class="framework-steps" id="fw-steps"></div>
|
||
</div>
|
||
|
||
<!-- DOGFOOD -->
|
||
<div class="st">🐕 Preuve par l'exemple <span class="suf">— WEVAL applique son propre framework (dogfooding)</span></div>
|
||
<div class="dogfood">
|
||
<div class="icon">🎯</div>
|
||
<div>
|
||
<h3>WEVAL utilise son propre framework</h3>
|
||
<p id="dog-pitch">Chargement…</p>
|
||
<p style="margin-top:8px"><a href="/weval-technology-platform.html" style="color:var(--accent);text-decoration:none;font-weight:600">→ Voir WEVAL Technology Platform (WTP)</a> qui applique ces agents sur nos propres pain points.</p>
|
||
</div>
|
||
<div class="stat">
|
||
<div class="stat-v" id="dog-sav">—</div>
|
||
<div class="stat-l">Savings WEVAL interne/an</div>
|
||
<div class="stat-v" id="dog-gaps" style="margin-top:10px">—</div>
|
||
<div class="stat-l">Gaps WEVAL comblés</div>
|
||
</div>
|
||
</div>
|
||
<!-- V96 19avr: SCAN PANEL — Gaps détectés depuis sources web (LLM+RSS+Playwright) -->
|
||
<section style="margin-top:42px;padding:24px 26px;background:linear-gradient(135deg,rgba(168,85,247,0.08),rgba(6,182,212,0.06));border:1px solid rgba(168,85,247,0.3);border-radius:14px">
|
||
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px;flex-wrap:wrap;gap:12px">
|
||
<div>
|
||
<h2 style="font-size:22px;font-weight:800;background:linear-gradient(90deg,#a855f7,#06b6d4);-webkit-background-clip:text;background-clip:text;color:transparent;margin-bottom:4px">🛰️ Scan Panel · Gaps détectés automatiquement</h2>
|
||
<p style="color:var(--dim);font-size:13px;max-width:780px">Pipeline 3-sources: <strong style="color:#a855f7">LLM</strong> (Cerebras sovereign 0€) · <strong style="color:#14b8a6">RSS</strong> (CIO, ERPToday, Reddit) · <strong style="color:#06b6d4">Playwright</strong> (TrustRadius reviews). Cron hebdo dimanche 3h. <span class="pulse"></span> données live · <a href="/api/erp-gap-scans.php" style="color:var(--accent)" target="_blank">JSON API</a></p>
|
||
</div>
|
||
<button id="scan-refresh-btn" style="padding:8px 14px;background:linear-gradient(135deg,#a855f7,#06b6d4);border:none;color:#0b0d15;border-radius:8px;font-weight:700;cursor:pointer;font-size:12px">🔄 Refresh</button>
|
||
</div>
|
||
<div id="scan-stats" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:16px"></div>
|
||
<div id="scan-per-erp" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px;margin-bottom:20px"></div>
|
||
<details style="margin-top:8px">
|
||
<summary style="cursor:pointer;color:var(--dim);font-size:13px;font-weight:600;user-select:none">📋 Voir les 20 gaps les plus confiants (détails)</summary>
|
||
<div id="scan-details" style="margin-top:12px;display:grid;gap:8px"></div>
|
||
</details>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
// V96: Load scan data from /api/erp-gap-scans.php
|
||
async function loadScanPanel() {
|
||
try {
|
||
const r = await fetch('/api/erp-gap-scans.php?limit=20&min_conf=0.5&t=' + Date.now());
|
||
const d = await r.json();
|
||
if (!d.stats) return;
|
||
const s = d.stats;
|
||
// Stats tiles
|
||
document.getElementById('scan-stats').innerHTML = [
|
||
['Total gaps', s.total_gaps, '#a855f7'],
|
||
['ERPs couverts', s.erps_covered + '/25', '#06b6d4'],
|
||
['Confiance moy.', s.avg_confidence, '#14b8a6'],
|
||
['Dernière scan', (s.last_scan_at||'?').substring(0,16).replace('T',' '), '#eab308'],
|
||
['LLM (D)', s.gaps_llm, '#a855f7'],
|
||
['RSS (C)', s.gaps_rss, '#14b8a6'],
|
||
['Playwright (B)', s.gaps_playwright, '#06b6d4']
|
||
].map(([lbl,val,color]) => `<div style="background:var(--bg-2);border:1px solid var(--border);border-radius:10px;padding:12px 14px;position:relative;overflow:hidden"><div style="position:absolute;left:0;top:0;width:3px;height:100%;background:${color}"></div><div style="color:var(--dim);font-size:10px;text-transform:uppercase;letter-spacing:.5px;font-weight:600;margin-bottom:4px">${lbl}</div><div style="font-size:20px;font-weight:800;color:var(--text)">${val}</div></div>`).join('');
|
||
// Per-ERP grid
|
||
document.getElementById('scan-per-erp').innerHTML = d.per_erp.map(e => {
|
||
const _rawSrc=e.sources;const _srcArr=Array.isArray(_rawSrc)?_rawSrc:(typeof _rawSrc==='string'?_rawSrc.split(/[,;|]/).map(function(s){return s.trim();}):(_rawSrc&&typeof _rawSrc==='object'?Object.values(_rawSrc).map(function(v){return String(v);}):[]));const srcs=_srcArr.filter(function(x){return x&&x!=='Other';}).join(' · ')||'Other';
|
||
const conf = parseFloat(e.avg_conf)||0;
|
||
const confColor = conf >= 0.7 ? '#22c55e' : (conf >= 0.5 ? '#eab308' : '#f43f5e');
|
||
return `<div style="background:var(--bg-2);border:1px solid var(--border);border-radius:8px;padding:10px 12px;display:flex;justify-content:space-between;align-items:center;gap:8px" onmouseover="this.style.borderColor='var(--accent)'" onmouseout="this.style.borderColor='var(--border)'">
|
||
<div style="flex:1;min-width:0;overflow:hidden">
|
||
<div style="font-weight:700;font-size:12.5px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${e.erp_name || e.erp_id}</div>
|
||
<div style="font-size:10px;color:var(--dim);margin-top:2px">${srcs}</div>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
|
||
<div style="color:${confColor};font-size:11px;font-weight:700">${conf}</div>
|
||
<div style="background:${confColor};color:#0b0d15;border-radius:100px;padding:2px 8px;font-size:11px;font-weight:800">${e.gaps_count}</div>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
// Details
|
||
document.getElementById('scan-details').innerHTML = (d.gaps||[]).slice(0,20).map(g => {
|
||
const srcColor = g.source==='LLM'?'#a855f7' : g.source==='RSS'?'#14b8a6' : g.source==='Playwright'?'#06b6d4' : '#64748b';
|
||
const linkPart = (g.source_url && !g.source_url.startsWith('llm://')) ? `<a href="${g.source_url}" target="_blank" rel="noopener" style="color:var(--accent);font-size:10px;margin-left:8px">↗ source</a>` : '';
|
||
return `<div style="background:var(--bg-1);border-left:3px solid ${srcColor};border-radius:4px;padding:9px 14px;font-size:12px">
|
||
<div style="display:flex;justify-content:space-between;gap:10px;margin-bottom:3px;flex-wrap:wrap">
|
||
<div><span style="font-weight:700;color:var(--text)">${g.erp_name || g.erp_id}</span> <span style="color:${srcColor};font-size:10px;font-weight:600;margin-left:4px">${g.source}</span> <span style="color:var(--mute);font-size:10px">· conf=${g.confidence_score}</span>${linkPart}</div>
|
||
</div>
|
||
<div style="color:var(--dim);font-size:11.5px;line-height:1.5">${(g.snippet||g.title||'').substring(0,280)}${(g.snippet||'').length>280?'…':''}</div>
|
||
</div>`;
|
||
}).join('') || '<div style="color:var(--dim);font-size:12px">Aucun gap (min_conf=0.5). Baissez le seuil via API: <code>?min_conf=0.3</code></div>';
|
||
} catch(e) { console.error('scan-panel:', e); }
|
||
}
|
||
document.getElementById('scan-refresh-btn').onclick = loadScanPanel;
|
||
loadScanPanel();
|
||
setInterval(loadScanPanel, 90000);
|
||
</script>
|
||
|
||
<script>
|
||
const API = '/api/wevia-v66-all-erps-painpoints.php';
|
||
let DATA = null;
|
||
let currentFilter = 'all';
|
||
|
||
async function load(){
|
||
try {
|
||
const r = await fetch(API + '?t=' + Date.now());
|
||
DATA = await r.json(); window.DATA = DATA;
|
||
render();
|
||
} catch(e) { console.error(e); }
|
||
}
|
||
|
||
function fmtEur(n){
|
||
if (n >= 1000000) return (n/1000000).toFixed(2) + 'M€';
|
||
if (n >= 1000) return (n/1000).toFixed(0) + 'k€';
|
||
return n + '€';
|
||
}
|
||
|
||
function render(){
|
||
if (!DATA) return;
|
||
const s = DATA.summary;
|
||
|
||
if (DATA.pitch) document.getElementById('pitch-t').textContent = '💡 ' + DATA.pitch;
|
||
|
||
document.getElementById('k-erps').textContent = s.erp_vendors_covered;
|
||
document.getElementById('k-pp').textContent = s.pain_points_total;
|
||
document.getElementById('k-ag').textContent = s.agents_unique;
|
||
document.getElementById('k-avg').innerHTML = Math.round(s.avg_savings_per_agent_eur/1000) + '<span class="u">k€/an</span>';
|
||
document.getElementById('k-tot').innerHTML = s.savings_per_client_year_m_eur + '<span class="u">M€/an</span>';
|
||
document.getElementById('k-tot-sub').textContent = s.savings_per_client_year_eur.toLocaleString('fr-FR') + ' €/an (tous agents déployés)';
|
||
|
||
// ERPs grid
|
||
const erps = DATA.erp_vendors || {};
|
||
const mentions = DATA.erp_mentions_top || {};
|
||
const erpWrap = document.getElementById('erp-grid');
|
||
erpWrap.innerHTML = Object.entries(erps).map(([k, v]) => {
|
||
const count = mentions[k] || 0;
|
||
return `<div class="erp-card">
|
||
<div class="tier ${v.tier.replace('_','-')}">${v.tier}</div>
|
||
<div class="name">${v.label}</div>
|
||
<div class="count">🚧 ${count} gap${count>1?'s':''}</div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// Filter bar
|
||
const depts = [...new Set((DATA.pain_points||[]).map(p => p.dept))];
|
||
const deptLabels = {
|
||
finance:'💰 Finance',
|
||
supply:'📦 Supply',
|
||
manufacturing:'🏭 Manufacturing',
|
||
sales:'💼 Sales/CRM',
|
||
hr:'👥 HR',
|
||
marketing:'📈 Marketing',
|
||
security:'🔐 Security',
|
||
operations:'⚙️ IT Ops',
|
||
direction:'👔 Direction'
|
||
};
|
||
const fBar = document.getElementById('filter-bar');
|
||
fBar.innerHTML = `<button class="filter-pill ${currentFilter==='all'?'active':''}" data-f="all">🌐 Tous (${DATA.pain_points.length})</button>` +
|
||
depts.map(d => {
|
||
const count = DATA.pain_points.filter(p => p.dept === d).length;
|
||
return `<button class="filter-pill ${currentFilter===d?'active':''}" data-f="${d}">${deptLabels[d]||d} (${count})</button>`;
|
||
}).join('');
|
||
fBar.querySelectorAll('.filter-pill').forEach(b => b.onclick = () => {
|
||
currentFilter = b.dataset.f;
|
||
renderPP();
|
||
fBar.querySelectorAll('.filter-pill').forEach(x => x.classList.toggle('active', x.dataset.f === currentFilter));
|
||
});
|
||
|
||
renderPP();
|
||
|
||
// Framework
|
||
const fw = DATA.arch_injection && DATA.arch_injection.framework;
|
||
if (fw) {
|
||
document.getElementById('fw-desc').textContent = fw.pitch;
|
||
document.getElementById('fw-steps').innerHTML = (fw.steps||[]).map((s,i) => `<div class="fw-step"><div class="fw-step-num">${i+1}</div><div class="fw-step-text">${s.replace(/^\d+\.\s*/,'')}</div></div>`).join('');
|
||
}
|
||
|
||
// Dogfood
|
||
const dog = DATA.dogfood_weval;
|
||
if (dog) {
|
||
document.getElementById('dog-pitch').textContent = dog.pitch;
|
||
document.getElementById('dog-sav').textContent = fmtEur(dog.weval_self_savings_eur_year);
|
||
document.getElementById('dog-gaps').textContent = dog.weval_self_gaps_fixed;
|
||
}
|
||
}
|
||
|
||
function renderPP(){
|
||
const wrap = document.getElementById('pp-grid');
|
||
let pps = DATA.pain_points || [];
|
||
if (currentFilter !== 'all') pps = pps.filter(p => p.dept === currentFilter);
|
||
pps = pps.sort((a,b) => b.savings_eur_year - a.savings_eur_year);
|
||
|
||
wrap.innerHTML = pps.map(p => `
|
||
<div class="pp-card">
|
||
<div class="pp-head">
|
||
<div class="pp-id">${p.id}</div>
|
||
<div class="pp-dept-tag">${p.dept}</div>
|
||
</div>
|
||
<div class="pp-pain">${p.pain}</div>
|
||
<div class="pp-biz">${p.business_cost}</div>
|
||
<div class="pp-why"><strong>Pourquoi l'ERP échoue</strong>${p.why_erp_fails}</div>
|
||
<div class="pp-arrow">↓</div>
|
||
<div class="pp-agent-box">
|
||
<div class="pp-agent-label">Agent WEVAL</div>
|
||
<div class="pp-agent-name">${p.agent}</div>
|
||
<div class="pp-agent-how">${p.agent_how}</div>
|
||
</div>
|
||
<div class="pp-savings">
|
||
<div>
|
||
<div class="pp-sav-lbl">Savings estimés</div>
|
||
<div style="font-size:9.5px;color:var(--dim);margin-top:3px">${p.savings_note}</div>
|
||
</div>
|
||
<div class="pp-sav-val">${fmtEur(p.savings_eur_year)}/an</div>
|
||
</div>
|
||
<div class="pp-erps">
|
||
${(p.erps_affected||[]).slice(0,6).map(e => `<span>${(DATA.erp_vendors[e]||{}).label || e}</span>`).join('')}
|
||
${(p.erps_affected||[]).length > 6 ? `<span>+${(p.erps_affected||[]).length-6}</span>` : ''}
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
load();
|
||
|
||
|
||
|
||
// === OPUS DRILL-DOWN PILOT v1 19avr — append-only, doctrine #14 ===
|
||
(function(){
|
||
if (window.__opusDrillInit) return; window.__opusDrillInit = true;
|
||
var modal = document.createElement('div');
|
||
modal.id = 'opus-drill-modal';
|
||
modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.85);backdrop-filter:blur(8px);display:none;align-items:center;justify-content:center;z-index:9999;padding:24px;cursor:pointer';
|
||
var inner = document.createElement('div');
|
||
inner.id = 'opus-drill-inner';
|
||
inner.style.cssText = 'max-width:820px;width:100%;max-height:90vh;overflow:auto;background:var(--bg-1,#0b0d15);border:1px solid var(--border-h,rgba(99,102,241,0.35));border-radius:16px;padding:32px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6)';
|
||
inner.addEventListener('click', function(e){ e.stopPropagation(); });
|
||
modal.appendChild(inner);
|
||
modal.addEventListener('click', function(){ modal.style.display='none'; });
|
||
document.addEventListener('keydown', function(e){ if(e.key==='Escape') modal.style.display='none'; });
|
||
if (document.body) document.body.appendChild(modal);
|
||
else document.addEventListener('DOMContentLoaded', function(){ document.body.appendChild(modal); });
|
||
|
||
function fmtEur(n){ if(!n) return '0€'; if(n>=1e6) return (n/1e6).toFixed(2)+'M€'; if(n>=1e3) return Math.round(n/1000)+'k€'; return n+'€'; }
|
||
|
||
function renderPP(pp, DATA) {
|
||
var vendors = (DATA && DATA.erp_vendors) || {};
|
||
var erps = (pp.erps_affected || []).map(function(e){
|
||
var v = vendors[e] || {};
|
||
return '<span style="display:inline-block;padding:4px 10px;margin:3px;background:rgba(99,102,241,0.08);border:1px solid rgba(99,102,241,0.25);border-radius:6px;font-size:11px;color:#e2e8f0">'+(v.label || e)+'</span>';
|
||
}).join('');
|
||
return [
|
||
'<div style="display:flex;gap:12px;margin-bottom:20px;align-items:center;flex-wrap:wrap">',
|
||
'<span style="font-family:JetBrains Mono,monospace;color:#eab308;font-weight:700;font-size:16px">'+pp.id+'</span>',
|
||
'<span style="padding:4px 12px;background:rgba(168,85,247,0.15);color:#d4a7fa;border-radius:20px;font-size:10.5px;text-transform:uppercase;letter-spacing:1px;font-weight:700">'+pp.dept+'</span>',
|
||
'<button onclick="document.getElementById(\'opus-drill-modal\').style.display=\'none\'" style="margin-left:auto;padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.15);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button>',
|
||
'</div>',
|
||
'<h2 style="font-size:22px;margin-bottom:8px;color:#e2e8f0;line-height:1.35">'+pp.pain+'</h2>',
|
||
'<p style="color:#94a3b8;margin-bottom:24px;font-size:13.5px;line-height:1.55">'+pp.business_cost+'</p>',
|
||
'<div style="margin-bottom:20px"><div style="color:#ef4444;font-size:11px;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;font-weight:700">Pourquoi l\'ERP échoue</div><p style="font-size:13px;line-height:1.55;color:#cbd5e1">'+pp.why_erp_fails+'</p></div>',
|
||
'<div style="margin-bottom:20px;padding:18px;background:rgba(20,184,166,0.06);border:1px solid rgba(20,184,166,0.25);border-radius:12px">',
|
||
'<div style="color:#14b8a6;font-size:11px;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;font-weight:700">🤖 Agent WEVAL</div>',
|
||
'<h4 style="font-size:16px;margin-bottom:6px;color:#e2e8f0">'+pp.agent+'</h4>',
|
||
'<p style="color:#94a3b8;font-size:12.5px;line-height:1.55">'+pp.agent_how+'</p>',
|
||
'</div>',
|
||
'<div style="padding:18px;background:linear-gradient(135deg,rgba(234,179,8,0.12),rgba(168,85,247,0.08));border:1px solid rgba(234,179,8,0.3);border-radius:12px;margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap">',
|
||
'<div><div style="font-size:11px;text-transform:uppercase;letter-spacing:1px;color:#94a3b8;margin-bottom:4px;font-weight:700">Savings estimés</div><div style="font-size:13px;color:#94a3b8">'+(pp.savings_note || '')+'</div></div>',
|
||
'<div style="font-size:30px;font-weight:800;color:#eab308;font-family:JetBrains Mono,monospace">'+fmtEur(pp.savings_eur_year)+'<span style="font-size:14px;color:#94a3b8">/an</span></div>',
|
||
'</div>',
|
||
'<div><div style="font-size:11px;text-transform:uppercase;letter-spacing:1px;color:#94a3b8;margin-bottom:10px;font-weight:700">ERPs affectés ('+(pp.erps_affected||[]).length+')</div><div>'+erps+'</div></div>'
|
||
].join('');
|
||
}
|
||
|
||
document.addEventListener('click', function(e) {
|
||
var card = e.target.closest && e.target.closest('.pp-card');
|
||
if (!card || !card.dataset || !card.dataset.ppId) return;
|
||
var DATA = window.DATA || null;
|
||
if (!DATA) return;
|
||
var pp = (DATA.pain_points || []).find(function(p){ return p.id === card.dataset.ppId; });
|
||
if (!pp) return;
|
||
document.getElementById('opus-drill-inner').innerHTML = renderPP(pp, DATA);
|
||
modal.style.display = 'flex';
|
||
});
|
||
|
||
var observer = new MutationObserver(function(){
|
||
var cards = document.querySelectorAll('.pp-card:not([data-pp-id])');
|
||
cards.forEach(function(card){
|
||
var idEl = card.querySelector('.pp-id');
|
||
if (idEl) {
|
||
card.dataset.ppId = idEl.textContent.trim();
|
||
card.style.cursor = 'pointer';
|
||
card.setAttribute('role', 'button');
|
||
card.setAttribute('tabindex', '0');
|
||
card.setAttribute('aria-label', 'Cliquer pour voir détails pain point');
|
||
}
|
||
});
|
||
});
|
||
observer.observe(document.body || document.documentElement, {childList:true, subtree:true});
|
||
})();
|
||
// === OPUS DRILL-DOWN PILOT END ===
|
||
|
||
</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>
|