diff --git a/api/ambre-kill25.php b/api/ambre-kill25.php new file mode 100644 index 000000000..ac553bc74 --- /dev/null +++ b/api/ambre-kill25.php @@ -0,0 +1,5 @@ +&1"); +echo "killed\n"; +echo @shell_exec("pgrep -af playwright | head -5"); diff --git a/api/ambre-pw-tests/output/.playwright-artifacts-0/page@0f3e0ae59c574465f872a8b6b62f1bd0.webm b/api/ambre-pw-tests/output/.playwright-artifacts-0/page@0f3e0ae59c574465f872a8b6b62f1bd0.webm deleted file mode 100644 index 9d6b10769..000000000 Binary files a/api/ambre-pw-tests/output/.playwright-artifacts-0/page@0f3e0ae59c574465f872a8b6b62f1bd0.webm and /dev/null differ diff --git a/api/ambre-pw-tests/output/.playwright-artifacts-0/page@98b514225ae9d8c4a090ae10741d9292.webm b/api/ambre-pw-tests/output/.playwright-artifacts-0/page@98b514225ae9d8c4a090ae10741d9292.webm new file mode 100644 index 000000000..e69de29bb diff --git a/api/ambre-pw-tests/output/v25-00-landing.png b/api/ambre-pw-tests/output/v25-00-landing.png deleted file mode 100644 index 2c7d2d014..000000000 Binary files a/api/ambre-pw-tests/output/v25-00-landing.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v25-01-01-intro.png b/api/ambre-pw-tests/output/v25-01-01-intro.png deleted file mode 100644 index 1df1b72c4..000000000 Binary files a/api/ambre-pw-tests/output/v25-01-01-intro.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v25-02-02-identity.png b/api/ambre-pw-tests/output/v25-02-02-identity.png deleted file mode 100644 index 2b424a1df..000000000 Binary files a/api/ambre-pw-tests/output/v25-02-02-identity.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v25-03-03-calc.png b/api/ambre-pw-tests/output/v25-03-03-calc.png deleted file mode 100644 index 3706f6785..000000000 Binary files a/api/ambre-pw-tests/output/v25-03-03-calc.png and /dev/null differ diff --git a/api/ambre-pw-tests/output/v25-04-04-qr.png b/api/ambre-pw-tests/output/v25-04-04-qr.png deleted file mode 100644 index bdc418804..000000000 Binary files a/api/ambre-pw-tests/output/v25-04-04-qr.png and /dev/null differ diff --git a/api/ambre-pw-tests/tests/v25-final.spec.js b/api/ambre-pw-tests/tests/v25-final.spec.js deleted file mode 100644 index fd1b425cd..000000000 --- a/api/ambre-pw-tests/tests/v25-final.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -const { test } = require("@playwright/test"); -const fs = require("fs"); - -test("V25 · FINAL · after mermaid fix · 10 turns incl PDF Premium · video longue", async ({ page }) => { - test.setTimeout(900000); - - const errors = []; - page.on("pageerror", e => errors.push("PE: " + e.message.substring(0,200))); - page.on("console", msg => { - if (msg.type() === "error" || msg.type() === "warning") { - errors.push(msg.type() + ": " + msg.text().substring(0,200)); - } - }); - - await page.goto("/wevia.html"); - await page.evaluate(() => { try { sessionStorage.clear(); } catch(e){} }); - await page.waitForLoadState("networkidle"); - await page.waitForTimeout(3000); - - console.log("=== Errors on load ==="); - errors.forEach(e => console.log(" ", e)); - - await page.screenshot({ path: "output/v25-00-landing.png" }); - - // Check global functions - const funcs = await page.evaluate(() => ({ - sendMsg: typeof window.sendMsg, - send: typeof window.send, - addMsg: typeof window.addMsg, - })); - console.log("\n=== Global functions ===", JSON.stringify(funcs)); - - const turns = [ - { label: "01-intro", msg: "Bonjour", needle: /aider|ravi|bienvenue/i }, - { label: "02-identity", msg: "je m'appelle Claire, directrice innovation chez BNP Paribas", needle: /Claire/i }, - { label: "03-calc", msg: "calcule (789 * 1.15 + 250) / 4", needle: /\d{3,}/ }, - { label: "04-qr", msg: "QR code pour https://bnpparibas.com", needle: /wevia-qr-|📱/i }, - { label: "05-pdf-premium", msg: "cree un pdf premium sur: strategie IA banque 2026 avec graphiques", needle: /pdf-premium|Chart\.js|Télécharger/i }, - { label: "06-search", msg: "actualités fintech 2026", needle: /🔍|source|fintech/i }, - { label: "07-recall", msg: "comment je m'appelle et où je travaille?", needle: /Claire.*BNP|BNP.*Claire/i }, - { label: "08-emotion", msg: "je suis un peu débordée avec tous mes projets", needle: /comprends|pression|gérer|pause/i }, - { label: "09-subject", msg: "change de sujet, parle moi des dauphins", needle: /dauphin|cétacé|mammifère|intelligent|océan/i }, - { label: "10-bilan", msg: "fais le bilan de notre échange aujourd'hui", needle: /Claire|BNP|PDF|IA|dauphin|calc|QR/i }, - ]; - - const results = []; - - for (let i = 0; i < turns.length; i++) { - const t = turns[i]; - const num = String(i + 1).padStart(2, "0"); - console.log(`\n═══ [${num}/${turns.length}] ${t.label} ═══`); - console.log(` → ${t.msg.substring(0, 100)}`); - - const beforeCount = await page.evaluate(() => document.querySelectorAll('.msg.assistant').length); - - try { - const input = page.locator("#msgInput"); - await input.click({ force: true }); - await page.keyboard.press("Control+A"); - await page.keyboard.press("Delete"); - await input.fill(t.msg); - await page.waitForTimeout(500); - await input.press("Enter"); - - const timeout = t.label.includes("pdf-premium") ? 90000 : 50000; - const waitStart = Date.now(); - let found = false; - let assistantReply = ""; - - while (Date.now() - waitStart < timeout) { - const state = await page.evaluate(() => { - const a = Array.from(document.querySelectorAll(".msg.assistant .bubble")); - return { - count: a.length, - last: a.length > 0 ? a[a.length-1].innerText : "", - }; - }); - - if (state.count > beforeCount) { - assistantReply = state.last; - // Skip if still showing error (may retry) - if (/erreur est survenue/i.test(assistantReply) && (Date.now() - waitStart) < 15000) { - await page.waitForTimeout(2500); - continue; - } - if (t.needle.test(assistantReply)) { found = true; break; } - if (assistantReply.length > 40 && !assistantReply.includes("erreur est")) { - // got real reply, check needle - if (t.needle.test(assistantReply)) { found = true; break; } - } - } - await page.waitForTimeout(2000); - } - - const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1); - const isError = /erreur est survenue/i.test(assistantReply); - - if (found) console.log(` ✅ PASS ${elapsed}s · ${assistantReply.substring(0,100)}`); - else if (isError) console.log(` ❌ ERROR ${elapsed}s`); - else console.log(` ⚠️ NO MATCH ${elapsed}s · reply: ${assistantReply.substring(0,150)}`); - - await page.evaluate(() => { const m = document.getElementById("messages"); if (m) m.scrollTop = m.scrollHeight; }); - await page.waitForTimeout(2500); - await page.screenshot({ path: `output/v25-${num}-${t.label}.png`, fullPage: false }); - - results.push({ turn: i+1, label: t.label, pass: found, error: isError, elapsed, reply_preview: assistantReply.substring(0,180) }); - await page.waitForTimeout(1500); - } catch (e) { - console.log(` ❌ EXCEPTION: ${e.message.substring(0, 120)}`); - results.push({ turn: i+1, label: t.label, pass: false, error: true }); - } - } - - await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); - await page.waitForTimeout(3000); - await page.screenshot({ path: "output/v25-99-final.png", fullPage: true }); - - const pass = results.filter(r=>r.pass).length; - const errors2 = results.filter(r=>r.error).length; - console.log(`\n═══════════════ V25 BILAN ═══════════════`); - console.log(`${pass}/${results.length} PASS · ${errors2} errors`); - results.forEach(r => console.log(` ${r.pass?"✅":(r.error?"❌":"⚠️")} T${r.turn} · ${r.label} · ${r.elapsed||"?"}s`)); - - fs.writeFileSync("output/v25-results.json", JSON.stringify({ results, pass, total: results.length, load_errors: errors }, null, 2)); -}); diff --git a/api/ambre-pw-tests/tests/v28-fetch-log.spec.js b/api/ambre-pw-tests/tests/v28-fetch-log.spec.js new file mode 100644 index 000000000..264eaeb8c --- /dev/null +++ b/api/ambre-pw-tests/tests/v28-fetch-log.spec.js @@ -0,0 +1,56 @@ +const { test } = require("@playwright/test"); + +test("V28 · wrap fetch and send HI", async ({ page }) => { + test.setTimeout(60000); + + const netlog = []; + const errs = []; + page.on("pageerror", e => errs.push(e.message)); + page.on("response", res => { + if (res.url().includes("/api/")) { + netlog.push({ url: res.url().split("?")[0], status: res.status() }); + } + }); + + await page.goto("/wevia.html"); + await page.waitForTimeout(2500); + + // Monkey-patch fetch to see all calls + await page.evaluate(() => { + window._fetchLog = []; + const orig = window.fetch; + window.fetch = function(u, opts) { + const url = typeof u === "string" ? u : u.url; + window._fetchLog.push({ url: url.split("?")[0], method: (opts && opts.method) || "GET" }); + return orig.apply(this, arguments); + }; + }); + + // Send "HI" + await page.fill("#msgInput", "HI"); + await page.waitForTimeout(300); + await page.press("#msgInput", "Enter"); + + await page.waitForTimeout(15000); + + const fetchLog = await page.evaluate(() => window._fetchLog); + console.log("\n=== fetch calls from JS ==="); + console.log(JSON.stringify(fetchLog, null, 2)); + + console.log("\n=== Network log (via Playwright) ==="); + console.log(JSON.stringify(netlog, null, 2)); + + console.log("\n=== Page errors ==="); + errs.forEach(e => console.log(" ", e.substring(0, 200))); + + // DOM state + const domState = await page.evaluate(() => { + const a = document.querySelectorAll(".msg.assistant .bubble"); + return { + count: a.length, + messages: Array.from(a).map(el => el.innerText.substring(0, 200)), + }; + }); + console.log("\n=== DOM messages ==="); + console.log(JSON.stringify(domState, null, 2)); +}); diff --git a/api/ambre-pw-v26-deploy.php b/api/ambre-pw-v26-deploy.php new file mode 100644 index 000000000..3ae8ea240 --- /dev/null +++ b/api/ambre-pw-v26-deploy.php @@ -0,0 +1,7 @@ + $written]); diff --git a/api/ambre-pw-v27-deploy.php b/api/ambre-pw-v27-deploy.php new file mode 100644 index 000000000..9f8798db4 --- /dev/null +++ b/api/ambre-pw-v27-deploy.php @@ -0,0 +1,7 @@ + $written]); diff --git a/api/ambre-pw-v28-deploy.php b/api/ambre-pw-v28-deploy.php new file mode 100644 index 000000000..12cad6c80 --- /dev/null +++ b/api/ambre-pw-v28-deploy.php @@ -0,0 +1,7 @@ + $written]); diff --git a/api/ambre-wiki-pdf-scan.php b/api/ambre-wiki-pdf-scan.php new file mode 100644 index 000000000..4331ac70c --- /dev/null +++ b/api/ambre-wiki-pdf-scan.php @@ -0,0 +1,43 @@ +[], "endpoints_pdf"=>[], "historic_pdf_mentions"=>[]]; + +// 1. Deep search obsidian vault +$search_terms = ["pdf premium", "pdf graphique", "pdf chart", "chart.js pdf", "pdf artefact", "reportlab chart", "matplotlib pdf", "weasyprint chart"]; +foreach (glob("/opt/obsidian-vault/**/*.md") as $f) { + $c = @file_get_contents($f); + if (!$c) continue; + foreach ($search_terms as $term) { + if (stripos($c, $term) !== false) { + $out["wiki_hits"][] = [ + "file" => str_replace("/opt/obsidian-vault/", "", $f), + "term" => $term, + "size" => strlen($c), + "mtime" => date("Y-m-d", filemtime($f)), + ]; + } + } +} + +// 2. All PDF-related endpoints +foreach (glob("/var/www/html/api/*pdf*.php") as $f) { + $out["endpoints_pdf"][] = ["name"=>basename($f), "size"=>filesize($f), "mtime"=>date("Y-m-d H:i", filemtime($f))]; +} +foreach (glob("/var/www/html/api/*doc*.php") as $f) { + $out["endpoints_pdf"][] = ["name"=>basename($f), "size"=>filesize($f), "mtime"=>date("Y-m-d H:i", filemtime($f))]; +} + +// 3. Existing generated PDFs recent +$gen_pdfs = []; +foreach (glob("/var/www/html/generated/*.pdf") as $f) { + $gen_pdfs[] = ["name"=>basename($f), "size_kb"=>round(filesize($f)/1024, 1), "mtime"=>date("Y-m-d H:i", filemtime($f))]; +} +usort($gen_pdfs, function($a,$b){return strcmp($b["mtime"], $a["mtime"]);}); +$out["recent_pdfs"] = array_slice($gen_pdfs, 0, 5); + +// 4. Look for any ambre-tool-pdf-premium.php current state +$pdf_prem = "/var/www/html/api/ambre-tool-pdf-premium.php"; +$out["pdf_premium_exists"] = file_exists($pdf_prem); +$out["pdf_premium_size"] = file_exists($pdf_prem) ? filesize($pdf_prem) : 0; + +echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); diff --git a/api/blade-actions-surfaced.json b/api/blade-actions-surfaced.json index 56a6ecc83..ea7405df0 100644 --- a/api/blade-actions-surfaced.json +++ b/api/blade-actions-surfaced.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-04-22T02:05:01.403657", + "generated_at": "2026-04-22T02:10:01.412228", "stats": { "total": 48, "pending": 31, diff --git a/api/growth-conversion-advisor.php b/api/growth-conversion-advisor.php new file mode 100644 index 000000000..3c9a52bc7 --- /dev/null +++ b/api/growth-conversion-advisor.php @@ -0,0 +1,133 @@ +'wepredict','name'=>'WePredict','url'=>'/wepredict.html','category'=>'prediction', + 'capability'=>'16 cockpits · 64 predictions · load/churn/ops forecast','status'=>'live','maturity'=>85, + 'use_for_conversion'=>'Anticipate deal close probability, churn warning, upsell timing'], + ['id'=>'dark_scout','name'=>'Dark Scout V83','url'=>'/v83-dark-scout-enriched.html','category'=>'intel', + 'capability'=>'Dark web + clearnet monitoring · threat + opportunity detection','status'=>'live','maturity'=>78, + 'use_for_conversion'=>'Detect client mentions, competitor pricing, early intent signals'], + ['id'=>'wevia_master','name'=>'WEVIA Master','url'=>'/wevia-master.html','category'=>'orchestrator', + 'capability'=>'269 tools Dynamic Resolver · 13 intents Wave 200 · 7 exec Wave 201','status'=>'live','maturity'=>90, + 'use_for_conversion'=>'Orchestrate multi-agent replies to prospects, auto-propose solutions'], + ['id'=>'arena','name'=>'WEVAL Arena','url'=>'/weval-arena.html','category'=>'command-center', + 'capability'=>'409 options · 715 enterprise agents · multi-model benchmarking','status'=>'live','maturity'=>80, + 'use_for_conversion'=>'A/B test LLM outputs for client docs, choose best model per use case'], + ['id'=>'ethica_b2b','name'=>'Ethica B2B HCP','url'=>'/consent.wevup.app','category'=>'data', + 'capability'=>'157K HCPs · 87% email coverage · 34 specialties · 12 sources','status'=>'live','maturity'=>82, + 'use_for_conversion'=>'Pharma client prospecting, targeted campaigns MENA/EU/US'], + ['id'=>'wevads_brain','name'=>'WEVADS Brain','url'=>'/brain-tower.html','category'=>'email-engine', + 'capability'=>'646 configs · 9 winners · PMTA+Kumo+Postfix triple','status'=>'live','maturity'=>95, + 'use_for_conversion'=>'Cold outreach industrialized · 9 sacred winners · deliverability 95%+'], + ['id'=>'blade_ai','name'=>'Blade AI','url'=>'/blade-ai.html','category'=>'ai-agent', + 'capability'=>'Web agent · desktop automation · 232 task heartbeat','status'=>'live','maturity'=>75, + 'use_for_conversion'=>'Auto-fill proposals, demo prep, competitor research, account creation'], + ['id'=>'paperclip','name'=>'Paperclip PM','url'=>'/paperclip.html','category'=>'project-mgmt', + 'capability'=>'848 agents · 6 projects · 9 goals · task flow','status'=>'live','maturity'=>65, + 'use_for_conversion'=>'Deal progression tracking, delivery SLA monitoring'], + ['id'=>'oss_stack','name'=>'OSS Sovereign Stack','url'=>'/api/oss-manifest.php','category'=>'toolchain', + 'capability'=>'10 OSS installed · 1748 MB · pandasai+Ollama, WeasyPrint, BioPython, Selenium, DocuSeal','status'=>'live','maturity'=>85, + 'use_for_conversion'=>'Generate proposals (WeasyPrint+DocuSeal), analyze data (pandasai+LLM), automate UI (Selenium)'], +]; + +// Effort/Impact matrix for top opportunities +$opportunities = [ + ['id'=>'vistex-cosumar','name'=>'Vistex SAP · Cosumar close','effort'=>3,'impact'=>9,'revenue_mad'=>450000, + 'status'=>'in_progress','time_days'=>14,'needs'=>['Lead addendum 0.8 DH/HCP counter','Portal demo'], + 'wevia_tools'=>['wevia_master','paperclip']], + ['id'=>'ethica-ma-contract','name'=>'Ethica Morocco · Kaouther Najar signing','effort'=>2,'impact'=>8,'revenue_mad'=>200000, + 'status'=>'in_progress','time_days'=>7,'needs'=>['Pilot consent ecm.py approval','Senders Arsenal'], + 'wevia_tools'=>['ethica_b2b','wevads_brain','wevia_master']], + ['id'=>'carrefour-retail','name'=>'Carrefour Morocco · CDC response','effort'=>5,'impact'=>8,'revenue_mad'=>350000, + 'status'=>'idea','time_days'=>21,'needs'=>['CDC specification','WeasyPrint proposal','Demo pharma+retail'], + 'wevia_tools'=>['oss_stack','wevia_master','paperclip']], + ['id'=>'api-hcp-package','name'=>'API HCP Maghreb · productize','effort'=>4,'impact'=>7,'revenue_mad'=>600000, + 'status'=>'in_progress','time_days'=>28,'needs'=>['Pricing tiers','Swagger docs','Stripe integration'], + 'wevia_tools'=>['ethica_b2b','arena']], + ['id'=>'weval-saas-freemium','name'=>'WEVAL SaaS Freemium launch','effort'=>6,'impact'=>9,'revenue_mad'=>800000, + 'status'=>'plan','time_days'=>45,'needs'=>['Landing','Stripe','Onboarding flow','FreePlan + Pro tier'], + 'wevia_tools'=>['wevia_master','blade_ai','oss_stack']], + ['id'=>'pharma-cloud-productize','name'=>'WEVAL Pharma Cloud productize','effort'=>5,'impact'=>8,'revenue_mad'=>500000, + 'status'=>'plan','time_days'=>30,'needs'=>['Package Ethica+WEVADS+Analytics','White-label','Partner channel'], + 'wevia_tools'=>['ethica_b2b','wevads_brain','wepredict']], + ['id'=>'linkedin-outbound','name'=>'LinkedIn outbound sequence','effort'=>1,'impact'=>5,'revenue_mad'=>80000, + 'status'=>'idea','time_days'=>3,'needs'=>['Selenium Blade sequencer','Copy 9 winners','Reply capture'], + 'wevia_tools'=>['blade_ai','wevads_brain']], + ['id'=>'stripe-consulting-pack','name'=>'Stripe Consulting pack Maghreb','effort'=>2,'impact'=>6,'revenue_mad'=>150000, + 'status'=>'idea','time_days'=>10,'needs'=>['Landing + calendly','Case studies','Stripe partner onboard'], + 'wevia_tools'=>['wevia_master']], + ['id'=>'seo-module-hub','name'=>'SEO Module Hub commercialization','effort'=>2,'impact'=>4,'revenue_mad'=>100000, + 'status'=>'plan','time_days'=>14,'needs'=>['Clients list','Pricing','Referral'],'wevia_tools'=>['paperclip']], + ['id'=>'huawei-refund','name'=>'Huawei Cloud refund (billing dispute)','effort'=>3,'impact'=>3,'revenue_mad'=>50000, + 'status'=>'in_progress','time_days'=>21,'needs'=>['Distributor switch','Docs'],'wevia_tools'=>['wevia_master']], +]; + +// Competitor matrix +$competitors = [ + ['category'=>'SAP Consulting Maghreb','competitors'=>['Vistex','Valoris','Capgemini MA'], + 'weval_edge'=>'SAP Ecosystem Partner · AI-augmented · sovereign stack 0€ inference','threat'=>'medium'], + ['category'=>'Pharma HCP Data MENA','competitors'=>['IQVIA','Veeva','Doctolib Pro'], + 'weval_edge'=>'157K HCPs · consent-first WevUp · 87% email coverage sovereign','threat'=>'low'], + ['category'=>'E-signatures MENA','competitors'=>['DocuSign','Yousign','HelloSign'], + 'weval_edge'=>'DocuSeal self-hosted · 0€ · data sovereignty MENA','threat'=>'high'], + ['category'=>'Email deliverability','competitors'=>['Mailgun','Sendgrid','Mailjet'], + 'weval_edge'=>'PMTA+Kumo+Postfix triple · 95%+ deliverability · own IPs','threat'=>'medium'], + ['category'=>'AI orchestration SMB','competitors'=>['Make.com','Zapier','n8n cloud'], + 'weval_edge'=>'WEVIA Master sovereign · 626 tools · 17 providers cascade 0€','threat'=>'low'], +]; + +// Focus & recommendations (effort/impact quadrants) +$quick_wins = array_filter($opportunities, function($o){ return $o['effort']<=3 && $o['impact']>=7; }); +$big_bets = array_filter($opportunities, function($o){ return $o['effort']>=4 && $o['impact']>=7; }); +$fill_ins = array_filter($opportunities, function($o){ return $o['effort']<=3 && $o['impact']<7; }); +$thankless = array_filter($opportunities, function($o){ return $o['effort']>=4 && $o['impact']<7; }); + +$total_revenue_mad_quick = array_sum(array_map(function($o){return $o['revenue_mad'];}, $quick_wins)); +$total_revenue_mad_big = array_sum(array_map(function($o){return $o['revenue_mad'];}, $big_bets)); + +echo json_encode([ + 'ts' => date('c'), + 'wave' => 228, + 'version' => 'deep-conversion-advisor-v1', + 'assets_count' => count($assets), + 'sovereign_ia' => $sovereign, + 'sovereign_ia_count' => count($sovereign), + 'opportunities' => array_values($opportunities), + 'matrix' => [ + 'quick_wins' => array_values($quick_wins), + 'big_bets' => array_values($big_bets), + 'fill_ins' => array_values($fill_ins), + 'thankless' => array_values($thankless), + ], + 'matrix_revenue' => [ + 'quick_wins_mad' => $total_revenue_mad_quick, + 'big_bets_mad' => $total_revenue_mad_big, + ], + 'competitors' => $competitors, + 'recommendations' => [ + [ 'rank'=>1, 'action'=>'Close Vistex Cosumar (7j)', 'why'=>'Quick win maximum · 450K MAD · need Yacine call Kaouther/Olga', 'deps'=>'Lead addendum 0.8 DH final answer'], + [ 'rank'=>2, 'action'=>'Sign Ethica Morocco pilot (7j)', 'why'=>'Sovereign stack showcase · Kaouther Najar · 200K MAD', 'deps'=>'ecm.py pilot consent approval'], + [ 'rank'=>3, 'action'=>'Launch LinkedIn outbound (3j)', 'why'=>'Low effort high cadence · Blade+WEVADS automation', 'deps'=>'Selenium sequencer + 9 winners copy'], + [ 'rank'=>4, 'action'=>'Productize API HCP Maghreb (28j)', 'why'=>'600K MAD annual recurring · Stripe ready', 'deps'=>'Pricing tiers + Swagger public'], + [ 'rank'=>5, 'action'=>'Launch WEVAL SaaS Freemium (45j)', 'why'=>'Big bet 800K MAD · showcase full stack', 'deps'=>'Landing + billing + onboarding'], + ], +], JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT); diff --git a/api/v83-business-kpi-latest.json b/api/v83-business-kpi-latest.json index 44be4ff9c..b9bacb789 100644 --- a/api/v83-business-kpi-latest.json +++ b/api/v83-business-kpi-latest.json @@ -1,7 +1,7 @@ { "ok": true, "version": "V83-business-kpi", - "ts": "2026-04-22T00:04:42+00:00", + "ts": "2026-04-22T00:09:16+00:00", "summary": { "total_categories": 8, "total_kpis": 64, diff --git a/growth-engine-v2.html b/growth-engine-v2.html index 8da5b66f4..483ecc68f 100644 --- a/growth-engine-v2.html +++ b/growth-engine-v2.html @@ -182,8 +182,8 @@ function buildKB(ops){ function build(){ const nav=document.getElementById('nav'); - const tabLabels={dashboard:'Dashboard',pipeline:'Pipeline CRM',plan90:'Plan 90J',social:'Réseaux & Canaux',scout:'Dark Scout',predict:'WePredict',connections:'Connexions'}; - const tabColors={dashboard:'var(--gold)',pipeline:'var(--em)',plan90:'var(--am)',social:'var(--sa)',scout:'var(--cy)',predict:'var(--ro)',connections:'var(--vi)'};/*V86*/ + const tabLabels={dashboard:'Dashboard',advisor:'🎯 Conversion Advisor',pipeline:'Pipeline CRM',plan90:'Plan 90J',social:'Réseaux & Canaux',scout:'Dark Scout',predict:'WePredict',connections:'Connexions'}; + const tabColors={dashboard:'var(--gold)',advisor:'#22d3ee',pipeline:'var(--em)',plan90:'var(--am)',social:'var(--sa)',scout:'var(--cy)',predict:'var(--ro)',connections:'var(--vi)'};/*V86*/ let nh=''; TABS.forEach((t,i)=>{ const on=i===0?' on':''; @@ -411,5 +411,131 @@ document.addEventListener('DOMContentLoaded',()=>{const s=document.createElement + + + + +