auto-commit via WEVIA vault_git intent 2026-04-18T13:23:22+00:00
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled

This commit is contained in:
opus
2026-04-18 15:23:22 +02:00
parent 86a2e683f9
commit 47d3f3f618
18 changed files with 211 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
{
"generated_at": "2026-04-18T15:15:01.952834",
"generated_at": "2026-04-18T15:20:01.907427",
"stats": {
"total": 153,
"pending": 268,

View File

@@ -0,0 +1,11 @@
{
"id": "task_20260418132002_356aea",
"name": "Blade self-heal 15:20",
"type": "powershell",
"command": "\n# Blade self-heal\nWrite-Host \"Self-heal triggered $(Get-Date)\"\n$agentProc = Get-Process powershell | Where-Object { $_.CommandLine -match 'sentinel-agent' }\nif (!$agentProc) {\n Write-Host \"Agent not running, starting...\"\n Start-Process powershell -ArgumentList \"-ExecutionPolicy\",\"Bypass\",\"-File\",\"C:\\ProgramData\\WEVAL\\sentinel-agent.ps1\" -WindowStyle Hidden\n}\n# Clear stale tasks > 3 days locally\n$cutoff = (Get-Date).AddDays(-3)\nGet-ChildItem \"C:\\ProgramData\\WEVAL\\tasks\\*.json\" -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt $cutoff } | Move-Item -Destination \"C:\\ProgramData\\WEVAL\\tasks\\archived\\\" -Force -ErrorAction SilentlyContinue\nWrite-Host \"Self-heal complete\"\n",
"cmd": "\n# Blade self-heal\nWrite-Host \"Self-heal triggered $(Get-Date)\"\n$agentProc = Get-Process powershell | Where-Object { $_.CommandLine -match 'sentinel-agent' }\nif (!$agentProc) {\n Write-Host \"Agent not running, starting...\"\n Start-Process powershell -ArgumentList \"-ExecutionPolicy\",\"Bypass\",\"-File\",\"C:\\ProgramData\\WEVAL\\sentinel-agent.ps1\" -WindowStyle Hidden\n}\n# Clear stale tasks > 3 days locally\n$cutoff = (Get-Date).AddDays(-3)\nGet-ChildItem \"C:\\ProgramData\\WEVAL\\tasks\\*.json\" -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt $cutoff } | Move-Item -Destination \"C:\\ProgramData\\WEVAL\\tasks\\archived\\\" -Force -ErrorAction SilentlyContinue\nWrite-Host \"Self-heal complete\"\n",
"priority": "high",
"status": "pending",
"created": "2026-04-18T13:20:02+00:00",
"created_by": "blade-control-ui"
}

View File

@@ -1,7 +1,7 @@
{
"ok": true,
"version": "V83-business-kpi",
"ts": "2026-04-18T13:15:14+00:00",
"ts": "2026-04-18T13:21:16+00:00",
"summary": {
"total_categories": 7,
"total_kpis": 56,

186
dsh-predict-widget.js Normal file
View File

@@ -0,0 +1,186 @@
/**
* DSH PREDICT Widget · WePredict v1.0 · 18avr2026
* Self-contained · zero external deps · safe to include anywhere.
* Fetches live data from WePredict endpoints and renders a glass-card panel.
* Yacine Mahboub · WEVAL Consulting
*/
(function(){
if (window.__DSH_PREDICT_LOADED__) return;
window.__DSH_PREDICT_LOADED__ = true;
const API_V71 = '/api/wevia-v71-intelligence-growth.php';
const API_HEAL = '/api/opus-arch-predictive-heal.php';
const API_CACHE = '/api/opus5-predictive-cache.php';
const API_GROWTH = '/api/growth-engine-api.php';
// ── Styles injection (scoped by .dsh-predict prefix) ──
const css = `
.dsh-predict-wrap{position:relative;margin:20px 0;font-family:-apple-system,Segoe UI,Roboto,sans-serif;color:#e6e9f0}
.dsh-predict-card{
background:linear-gradient(135deg,rgba(20,25,40,.72),rgba(35,20,60,.58));
backdrop-filter:blur(18px) saturate(1.2);-webkit-backdrop-filter:blur(18px) saturate(1.2);
border:1px solid rgba(120,140,255,.18);border-radius:20px;padding:22px 26px;
box-shadow:0 8px 32px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.05);
transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;
overflow:hidden
}
.dsh-predict-card:hover{transform:translateY(-3px);box-shadow:0 14px 42px rgba(80,100,255,.28);border-color:rgba(140,170,255,.38)}
.dsh-predict-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;gap:12px;flex-wrap:wrap}
.dsh-predict-title{font-size:15px;font-weight:600;letter-spacing:.5px;display:flex;align-items:center;gap:10px;margin:0}
.dsh-predict-title .emoji{font-size:22px}
.dsh-predict-title small{display:block;font-size:10px;font-weight:400;color:#8892b4;letter-spacing:1px;margin-top:2px}
.dsh-predict-badge{
display:inline-flex;align-items:center;gap:6px;padding:5px 12px;border-radius:999px;font-size:10px;
font-weight:700;letter-spacing:1.2px;text-transform:uppercase
}
.dsh-predict-badge.live{background:rgba(52,211,153,.15);color:#34d399;border:1px solid rgba(52,211,153,.35)}
.dsh-predict-badge.warn{background:rgba(250,204,21,.15);color:#facc15;border:1px solid rgba(250,204,21,.35)}
.dsh-predict-badge.alert{background:rgba(248,113,113,.15);color:#f87171;border:1px solid rgba(248,113,113,.45)}
.dsh-predict-badge .dot{width:7px;height:7px;border-radius:50%;background:currentColor;animation:dshPulse 1.8s ease-in-out infinite}
@keyframes dshPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.75)}}
.dsh-predict-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:14px}
.dsh-predict-tile{
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);border-radius:14px;
padding:14px 16px;transition:all .2s ease;cursor:default
}
.dsh-predict-tile:hover{background:rgba(255,255,255,.06);border-color:rgba(140,170,255,.25)}
.dsh-predict-tile .label{font-size:10px;text-transform:uppercase;letter-spacing:1px;color:#8892b4;margin-bottom:6px;font-weight:600}
.dsh-predict-tile .value{font-size:26px;font-weight:700;line-height:1;letter-spacing:-.5px}
.dsh-predict-tile .value.g{background:linear-gradient(135deg,#34d399,#60a5fa);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.dsh-predict-tile .value.w{color:#facc15}
.dsh-predict-tile .value.r{color:#f87171}
.dsh-predict-tile .sub{font-size:11px;color:#8892b4;margin-top:6px}
.dsh-predict-actions{margin-top:14px;padding-top:14px;border-top:1px solid rgba(255,255,255,.07)}
.dsh-predict-actions .label{font-size:10px;text-transform:uppercase;letter-spacing:1px;color:#8892b4;font-weight:600;margin-bottom:8px}
.dsh-predict-actions ul{margin:0;padding:0 0 0 18px;font-size:12px;color:#c5cbe0;line-height:1.7}
.dsh-predict-foot{margin-top:14px;font-size:10px;color:#5f6787;display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap}
.dsh-predict-foot a{color:#60a5fa;text-decoration:none}
.dsh-predict-foot a:hover{text-decoration:underline}
.dsh-predict-err{color:#f87171;font-size:12px;padding:10px;text-align:center}
.dsh-predict-skel{display:inline-block;width:60%;height:22px;background:linear-gradient(90deg,rgba(255,255,255,.05),rgba(255,255,255,.12),rgba(255,255,255,.05));background-size:200% 100%;animation:dshShim 1.5s ease-in-out infinite;border-radius:4px}
@keyframes dshShim{0%{background-position:200% 0}100%{background-position:-200% 0}}
`;
const st = document.createElement('style');
st.id='dsh-predict-style';st.textContent=css;
document.head.appendChild(st);
// ── Mount point: use #dsh-predict-mount if present, else inject before </body> ──
let mount = document.getElementById('dsh-predict-mount');
if (!mount) {
mount = document.createElement('div');
mount.id = 'dsh-predict-mount';
mount.className = 'dsh-predict-wrap';
// Insert inside main/container if available, else body end
const parent = document.querySelector('main, .main, .content, .container, #app') || document.body;
parent.appendChild(mount);
}
mount.classList.add('dsh-predict-wrap');
mount.innerHTML = `
<div class="dsh-predict-card" role="region" aria-label="DSH Predict live">
<div class="dsh-predict-head">
<div>
<p class="dsh-predict-title"><span class="emoji">🔮</span>DSH PREDICT <small>WePredict · growth · spy · heal</small></p>
</div>
<span id="dshp-badge" class="dsh-predict-badge live"><span class="dot"></span>LIVE</span>
</div>
<div class="dsh-predict-grid" id="dshp-grid">
<div class="dsh-predict-tile"><div class="label">Loading…</div><div class="value"><span class="dsh-predict-skel"></span></div></div>
</div>
<div class="dsh-predict-actions" id="dshp-actions" style="display:none">
<div class="label">Recommended actions</div>
<ul id="dshp-reco"></ul>
</div>
<div class="dsh-predict-foot">
<span id="dshp-ts">—</span>
<span>Sources: <a href="${API_V71}" target="_blank">v71 Growth</a> · <a href="${API_HEAL}" target="_blank">Heal</a> · <a href="${API_CACHE}" target="_blank">Cache</a></span>
</div>
</div>`;
// ── Fetch helpers ──
const fetchJSON = (url, timeout=8000) => new Promise((res)=>{
const ctrl = new AbortController();
const to = setTimeout(()=>ctrl.abort(), timeout);
fetch(url,{signal:ctrl.signal,cache:'no-store'})
.then(r=>r.ok?r.json():Promise.reject(r.status))
.then(d=>{clearTimeout(to);res({ok:true,data:d});})
.catch(e=>{clearTimeout(to);res({ok:false,err:String(e)});});
});
const fmt = (n) => {
if (n===null||n===undefined) return '—';
if (typeof n==='number'){
if (Math.abs(n)>=1e6) return (n/1e6).toFixed(1)+'M';
if (Math.abs(n)>=1e3) return (n/1e3).toFixed(1)+'k';
return Number.isInteger(n)?n.toString():n.toFixed(2);
}
return String(n);
};
// ── Render ──
async function render(){
const [r71, rheal, rcache] = await Promise.all([
fetchJSON(API_V71), fetchJSON(API_HEAL), fetchJSON(API_CACHE)
]);
const grid = document.getElementById('dshp-grid');
const badge = document.getElementById('dshp-badge');
const ts = document.getElementById('dshp-ts');
const actionsBox = document.getElementById('dshp-actions');
const reco = document.getElementById('dshp-reco');
if (!r71.ok && !rheal.ok && !rcache.ok) {
grid.innerHTML = `<div class="dsh-predict-err">⚠ Predict APIs unreachable. Retry soon.</div>`;
badge.className='dsh-predict-badge alert';badge.innerHTML='<span class="dot"></span>ALERT';
return;
}
const s = (r71.ok && r71.data && r71.data.summary) ? r71.data.summary : {};
const heal = rheal.ok ? rheal.data : {};
const cache = rcache.ok && rcache.data && rcache.data.stats ? rcache.data.stats : {};
// Tiles
const tiles = [
{ lbl:'Load · next hour', val:fmt(heal.predicted_next_hour), sub:(heal.alert?'⚠ over threshold':'safe · threshold '+(heal.threshold||5)), klass:heal.alert?'w':'g' },
{ lbl:'Competitors tracked', val:s.competitors_tracked||0, sub:`${s.competitors_high_threat||0} high threat`, klass:(s.competitors_high_threat>=3?'w':'g') },
{ lbl:'Opportunities · k€', val:fmt(s.opportunities_total_value_keur), sub:`${s.opportunities_high_urgency||0} high urgency`, klass:'g' },
{ lbl:'Innovations · 24h', val:s.innovations_last_24h||0, sub:`total ${s.innovations_total||0}`, klass:'g' },
{ lbl:'Chatbots deployed', val:`${s.chatbots_deployed||0}/${s.chatbots_total||0}`, sub:'lead capture hub', klass:'g' },
{ lbl:'Leads · 7d', val:s.leads_captured_7d||0, sub:(s.leads_captured_7d===0?'🔴 capture à relancer':'ok'), klass:(s.leads_captured_7d===0?'r':'g') },
{ lbl:'Agility agents · gap', val:s.agility_agents_gap||0, sub:`FTE savings ${s.agility_fte_savings_year||0}/an`, klass:(s.agility_agents_gap>=5?'w':'g') },
{ lbl:'Cache predict · hit%', val:(cache.hit_rate_pct!==undefined?cache.hit_rate_pct+'%':'—'), sub:`${cache.gets||0} gets · ${cache.hits||0} hits`, klass:'g' }
];
grid.innerHTML = tiles.map(t => `
<div class="dsh-predict-tile">
<div class="label">${t.lbl}</div>
<div class="value ${t.klass}">${t.val}</div>
<div class="sub">${t.sub}</div>
</div>`).join('');
// Badge status
const hasAlert = heal.alert || (s.leads_captured_7d===0) || (s.competitors_high_threat>=3);
const hasWarn = (s.opportunities_high_urgency>=3) || (s.agility_agents_gap>=5);
if (hasAlert) { badge.className='dsh-predict-badge alert'; badge.innerHTML='<span class="dot"></span>ALERT'; }
else if (hasWarn) { badge.className='dsh-predict-badge warn'; badge.innerHTML='<span class="dot"></span>WARN'; }
else { badge.className='dsh-predict-badge live'; badge.innerHTML='<span class="dot"></span>LIVE'; }
// Recommendations
const rec = [];
if (heal.alert && Array.isArray(heal.recommended_actions)) rec.push(...heal.recommended_actions);
if (s.leads_captured_7d===0) rec.push('Relancer campagne lead capture (0 leads 7j)');
if (s.competitors_high_threat>=3) rec.push(`${s.competitors_high_threat} concurrents high-threat · revoir positionnement`);
if (s.opportunities_high_urgency>=3) rec.push(`${s.opportunities_high_urgency} opportunités urgentes · prioriser`);
if (s.agility_agents_gap>=5) rec.push(`${s.agility_agents_gap} agents agility à créer · potentiel FTE ${s.agility_fte_savings_year||0}/an`);
if (rec.length) {
actionsBox.style.display='block';
reco.innerHTML = rec.slice(0,4).map(x=>`<li>${x}</li>`).join('');
} else {
actionsBox.style.display='none';
}
ts.textContent = 'Updated ' + new Date().toLocaleTimeString('fr-FR');
}
render();
// Refresh every 60s
setInterval(render, 60000);
})();

View File

@@ -120,4 +120,7 @@ h2{padding:12px 40px 0;font-size:15px;color:#7c3aed;text-transform:uppercase;let
})();
</script>
<!-- /CARTO_BANNER_V1 -->
</body></html>
<!-- DSH PREDICT 18avr26 -->
<script src="/dsh-predict-widget.js" defer></script>
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

View File

@@ -1329,5 +1329,8 @@ if (typeof window.navigateTo === 'function'){
</section>
<!-- DSH-PREDICT-v1 WIDGET END -->
</body>
<!-- DSH PREDICT 18avr26 -->
<script src="/dsh-predict-widget.js" defer></script>
</body>
</html>

View File

@@ -2963,5 +2963,8 @@ init();
</section>
<!-- DSH-PREDICT-v1 WIDGET END -->
</body>
<!-- DSH PREDICT 18avr26 -->
<script src="/dsh-predict-widget.js" defer></script>
</body>
</html>