Files
html/platform/index.html
2026-04-12 22:57:03 +02:00

652 lines
54 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<!-- cache-bust-meta v1773010147 -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVAL Platform — Products & Services AppStore</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.9/babel.min.js"></script>
<style>
:root{--bg:#05080f;--s1:#0c1222;--s2:#111a2e;--border:rgba(0,201,167,0.08);--t:#00c9a7;--t15:rgba(0,201,167,0.15);--p:#7c5cfc;--g:#f0c674;--co:#ff6b6b;--bl:#4ea8de;--or:#ff9f43;--si:#7a8ba5;--w:#edf2f7;--dk:#1a2340}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Outfit',sans-serif;background:var(--bg);color:var(--w);min-height:100vh}
body::before{content:'';position:fixed;inset:0;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");pointer-events:none;z-index:1000}
::-webkit-scrollbar{width:6px}
::-webkit-scrollbar-track{background:var(--bg)}
::-webkit-scrollbar-thumb{background:rgba(0,201,167,0.2);border-radius:3px}
a{color:var(--t);text-decoration:none}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useCallback, createContext, useContext } = React;
const API = '/api/platform/index.php';
const PRODUCT_URLS = {
arsenal: '/products/arsenal.html',
wevads: '/products/wevads.html',
deliverads: '/products/deliverads.html',
'wevia-enterprise': '/products/wevia-enterprise.html',
deliverscore: '/products/deliverscore.html',
'content-factory': '/products/content-factory.html',
medreach: '/products/medreach.html',
'gpu-inference': '/products/gpu-inference.html',
proposalai: '/products/proposalai.html',
blueprintai: '/products/blueprintai.html',
storeforge: '/products/storeforge.html',
'wevia-whitelabel': '/products/wevia-whitelabel.html',
leadforge: '/products/workspace.html',
formbuilder: '/products/workspace.html',
mailforge: '/products/mailforge.html',
'cloud-providers': '/products/cloud-providers.html',
mailwarm: '/products/workspace.html',
sentinel: '/products/workspace.html',
outreachai: '/products/workspace.html',
'wevads-affiliates': '/products/workspace.html',
devforge: '/products/workspace.html',
'youtube-factory': '/products/workspace.html',
emailverify: '/products/workspace.html',
blacklistguard: '/products/workspace.html',
reputationai: '/products/workspace.html',
copyai: '/products/workspace.html',
dataharvest: '/products/workspace.html',
newsletterinsight: '/products/workspace.html',
smsforge: '/products/workspace.html',
adscontrol: '/products/workspace.html',
wevalcrm: '/products/workspace.html',
cloudcost: '/products/workspace.html',
canvasai: '/products/workspace.html',
inboxtest: '/products/workspace.html',
ispmonitor: '/products/workspace.html',
presentationai: '/products/workspace.html',
dashboardai: '/products/workspace.html',
translateai: '/products/workspace.html',
businessplan: '/products/workspace.html',
contractai: '/products/workspace.html',
meetingsummary: '/products/workspace.html',
cloudbridge: '/products/cloud-providers.html',
esignature: '/products/workspace.html',
leansixsigma: '/products/workspace.html',
auditcompliance: '/products/workspace.html',
'weval-mind': '/products/workspace.html',
'creative-factory': '/products/workspace.html',
'scout-intelligence': '/products/workspace.html',
};
// ═══ AUTH CONTEXT ═══
const AuthCtx = createContext(null);
function useAuth() { return useContext(AuthCtx); }
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [token, setToken] = useState(localStorage?.getItem?.('wv_token') || null);
const [loading, setLoading] = useState(true);
const login = async (email, password) => {
const res = await fetch(`${API}?a=auth/login`, {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({ email, password })
});
const d = await res.json();
if (!d.ok) throw new Error(d.error);
try { localStorage.setItem('wv_token', d.token); } catch(e) {}
setToken(d.token);
setUser(d.user);
return d.user;
};
const register = async (data) => {
const res = await fetch(`${API}?a=auth/register`, {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(data)
});
const d = await res.json();
if (!d.ok) throw new Error(d.error);
try { localStorage.setItem('wv_token', d.token); } catch(e) {}
setToken(d.token);
setUser(d.user);
return d.user;
};
const logout = () => {
try { localStorage.removeItem('wv_token'); } catch(e) {}
setToken(null); setUser(null);
};
const apiFetch = useCallback(async (action, opts = {}) => {
const headers = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(`${API}?a=${action}`, { ...opts, headers: { ...headers, ...opts.headers } });
return res.json();
}, [token]);
useEffect(() => {
if (token) {
apiFetch('auth/me').then(d => { if (d.ok) setUser(d.user); else logout(); })
.catch(() => logout()).finally(() => setLoading(false));
} else setLoading(false);
}, [token]);
return React.createElement(AuthCtx.Provider, { value: { user, token, login, register, logout, apiFetch, loading } }, children);
}
// ═══ STYLES ═══
const css = {
nav: { position:'fixed',top:0,width:'100%',padding:'0.7rem 3%',display:'flex',justifyContent:'space-between',alignItems:'center',zIndex:100,backdropFilter:'blur(20px)',background:'rgba(5,8,15,0.85)',borderBottom:'1px solid var(--border)' },
logo: { fontWeight:800,fontSize:'1.3rem',letterSpacing:'-0.03em',cursor:'pointer' },
logoSpan: { color:'var(--t)' },
btn: { padding:'0.55rem 1.2rem',borderRadius:'8px',fontWeight:600,fontSize:'0.82rem',border:'none',cursor:'pointer',fontFamily:'Outfit,sans-serif',transition:'all 0.2s' },
btnT: { background:'var(--t)',color:'var(--bg)' },
btnO: { background:'transparent',color:'var(--w)',border:'1px solid rgba(255,255,255,0.12)' },
btnP: { background:'var(--p)',color:'white' },
card: { background:'var(--s1)',border:'1px solid var(--border)',borderRadius:'14px',padding:'1.5rem',transition:'all 0.3s',cursor:'pointer' },
input: { width:'100%',background:'rgba(0,0,0,0.3)',border:'1px solid rgba(255,255,255,0.08)',borderRadius:'8px',padding:'0.7rem 0.9rem',fontSize:'0.88rem',color:'var(--w)',fontFamily:'Outfit,sans-serif',outline:'none' },
label: { display:'block',fontSize:'0.7rem',fontWeight:600,textTransform:'uppercase',letterSpacing:'0.1em',color:'var(--si)',marginBottom:'0.35rem' },
badge: { fontFamily:'Space Mono,monospace',fontSize:'0.6rem',fontWeight:700,padding:'0.2rem 0.5rem',borderRadius:'100px' },
sidebar: { width:'240px',background:'var(--s1)',borderRight:'1px solid var(--border)',padding:'1rem 0',position:'fixed',top:'52px',bottom:0,overflowY:'auto' },
main: { marginLeft:'240px',padding:'2rem 2.5rem',paddingTop:'calc(52px + 2rem)',minHeight:'100vh' },
};
const colors = { flagship:'var(--g)', saas:'var(--t)', service:'var(--bl)' };
const catLabels = { flagship:'Flagship',saas:'SaaS',service:'Service' };
// ═══ COMPONENTS ═══
function Nav({ page, setPage }) {
const { user, logout } = useAuth();
return React.createElement('nav', { style: css.nav },
React.createElement('div', { style: css.logo, onClick: () => setPage('store') },
'WEVAL', React.createElement('span', { style: css.logoSpan }, 'Platform')
),
React.createElement('div', { style: { display:'flex',gap:'1rem',alignItems:'center' } },
user ? [
React.createElement('span', { key:'u', style:{fontSize:'0.82rem',color:'var(--si)'} }, user.name || user.email),
React.createElement('button', { key:'d', style:{...css.btn,...css.btnO,fontSize:'0.75rem',padding:'0.4rem 0.8rem'}, onClick:()=>setPage('dashboard') }, '📊 Dashboard'),
React.createElement('button', { key:'l', style:{...css.btn,...css.btnO,fontSize:'0.75rem',padding:'0.4rem 0.8rem'}, onClick:logout }, 'Déconnexion'),
] : [
React.createElement('button', { key:'li', style:{...css.btn,...css.btnO}, onClick:()=>setPage('login') }, 'Connexion'),
React.createElement('button', { key:'r', style:{...css.btn,...css.btnT}, onClick:()=>setPage('register') }, 'Créer un compte →'),
]
)
);
}
function SideBar({ page, setPage }) {
const { user } = useAuth();
const items = [
{ id:'store', icon:'🏪', label:'AppStore' },
{ id:'dashboard', icon:'📊', label:'Dashboard', auth:true },
{ id:'subscriptions', icon:'📦', label:'Mes Produits', auth:true },
{ id:'keys', icon:'🔑', label:'API Keys', auth:true },
{ id:'usage', icon:'📈', label:'Usage', auth:true },
];
if (user?.role === 'admin') {
items.push({ id:'admin', icon:'⚙️', label:'Admin', auth:true });
}
return React.createElement('div', { style: css.sidebar },
items.filter(i => !i.auth || user).map(i =>
React.createElement('div', {
key: i.id,
onClick: () => setPage(i.id),
style: {
padding:'0.65rem 1.2rem',display:'flex',alignItems:'center',gap:'0.6rem',
cursor:'pointer',fontSize:'0.85rem',fontWeight: page===i.id ? 600 : 400,
color: page===i.id ? 'var(--t)' : 'var(--si)',
background: page===i.id ? 'rgba(0,201,167,0.05)' : 'transparent',
borderRight: page===i.id ? '2px solid var(--t)' : '2px solid transparent',
transition:'all 0.2s'
}
}, i.icon, ' ', i.label)
)
);
}
// ═══ PAGES ═══
function StorePage({ setPage }) {
const { apiFetch, user } = useAuth();
const [products, setProducts] = useState([]);
const [filter, setFilter] = useState('all');
const [search, setSearch] = useState('');
useEffect(() => {
fetch(API+'?a=products&live=0').then(r=>r.json()).then(d => d.ok && setProducts(d.products.filter(x=>x.slug!=='ethica'))).catch(e=>console.error('Products load error:',e));
}, []);
const filtered = products.filter(p => {
if (filter !== 'all' && p.category !== filter) return false;
if (search && !p.name.toLowerCase().includes(search.toLowerCase()) && !p.tagline?.toLowerCase().includes(search.toLowerCase())) return false;
return true;
});
const flagships = filtered.filter(p => p.is_flagship && p.slug!=='ethica');
const saas = filtered.filter(p => !p.is_flagship && p.slug!=='ethica');
const subscribe = async (slug) => {
if (!user) { setPage('register'); return; }
const d = await apiFetch('subscribe', { method:'POST', body:JSON.stringify({product:slug,plan:'free'}) });
if (d.ok) { alert('✅ Abonné !'); }
};
return React.createElement('div', null,
// Hero
React.createElement('div', { style:{textAlign:'center',padding:'3rem 0 2rem'} },
React.createElement('div', { style:{fontFamily:'Space Mono',fontSize:'0.7rem',color:'var(--t)',letterSpacing:'0.15em',textTransform:'uppercase',marginBottom:'1rem'} }, '// WEVAL APPSTORE'),
React.createElement('h1', { style:{fontSize:'2.8rem',fontWeight:800,letterSpacing:'-0.04em',marginBottom:'0.8rem'} },
'Tous nos produits & services'
),
React.createElement('p', { style:{color:'var(--si)',fontSize:'1rem',maxWidth:'500px',margin:'0 auto 2rem'} },
`${products.length} produits — 4 flagships en production — APIs live`
),
),
// Filters
React.createElement('div', { style:{display:'flex',gap:'0.6rem',marginBottom:'2rem',flexWrap:'wrap',alignItems:'center'} },
React.createElement('input', {
style:{...css.input,maxWidth:'280px'},
placeholder:'🔍 Rechercher un produit...',
value:search, onChange:e=>setSearch(e.target.value)
}),
['all','flagship','saas'].map(f =>
React.createElement('button', {
key:f, onClick:()=>setFilter(f),
style:{...css.btn, ...(filter===f ? css.btnT : css.btnO), fontSize:'0.75rem',padding:'0.4rem 0.8rem'}
}, f === 'all' ? 'Tous' : catLabels[f] || f)
)
),
// Flagships
flagships.length > 0 && React.createElement('div', { style:{marginBottom:'2.5rem'} },
React.createElement('h2', { style:{fontSize:'1.3rem',fontWeight:700,marginBottom:'1rem'} }, '⭐ Flagships — En Production'),
React.createElement('div', { style:{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(280px,1fr))',gap:'1rem'} },
flagships.map(p => React.createElement(ProductCard, { key:p.id, product:p, onSubscribe:subscribe, setPage }))
)
),
// SaaS
saas.length > 0 && React.createElement('div', null,
React.createElement('h2', { style:{fontSize:'1.3rem',fontWeight:700,marginBottom:'1rem'} }, '🚀 Produits SaaS'),
React.createElement('div', { style:{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(270px,1fr))',gap:'1rem'} },
saas.map(p => React.createElement(ProductCard, { key:p.id, product:p, onSubscribe:subscribe, setPage }))
)
)
);
}
const PRODUCT_LOGOS = {
'arsenal': '<img src="/assets/logo-arsenal.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="arsenal">',
'wevads': '<img src="/assets/logo-wevads-Crayl4yz.png" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="wevads">',
'deliverads': '<img src="/assets/logo-deliverads.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="deliverads">',
'wevia-enterprise': '<img src="/assets/logo-weval-ia.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="wevia-enterprise">',
'deliverscore': '<img src="/assets/logo-deliverscore.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="deliverscore">',
'content-factory': '<img src="/assets/logo-content-factory.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="content-factory">',
'medreach': '<img src="/assets/logo-medreach.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="medreach">',
'gpu-inference': '<img src="/assets/logo-weval-ia.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="gpu-inference">',
'proposalai': '<img src="/assets/logo-proposalai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="proposalai">',
'blueprintai': '<img src="/assets/logo-blueprintai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="blueprintai">',
'storeforge': '<img src="/assets/logo-storeforge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="storeforge">',
'devforge': '<img src="/assets/logo-devforge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="devforge">',
'sentinel': '<img src="/assets/logo-sentinel.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="sentinel">',
'mailwarm': '<img src="/assets/logo-mailwarm.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="mailwarm">',
'outreachai': '<img src="/assets/logo-outreachai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="outreachai">',
'leadforge': '<img src="/assets/logo-leadforge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="leadforge">',
'formbuilder': '<img src="/assets/logo-formbuilder.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="formbuilder">',
'ethica': '<img src="/assets/logo-medreach.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="ethica">',
'youtube-factory': '<img src="/assets/logo-youtube-factory.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="youtube-factory">',
'affiliates': '<img src="/assets/logo-affiliates.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="affiliates">',
'wevia-whitelabel': '<img src="/assets/logo-wevia-whitelabel.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="wevia-whitelabel">',
'dataharvest': '<img src="/assets/logo-dataharvest.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="dataharvest">',
'newsletterspy': '<img src="/assets/logo-newsletterspy.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="newsletterspy">',
'smsforge': '<img src="/assets/logo-smsforge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="smsforge">',
'adscontrol': '<img src="/assets/logo-adscontrol.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="adscontrol">',
'weval-crm': '<img src="/assets/logo-weval-crm.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="weval-crm">',
'cloudcost': '<img src="/assets/logo-cloudcost.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="cloudcost">',
'canvasai': '<img src="/assets/logo-canvasai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="canvasai">',
'inboxtest': '<img src="/assets/logo-inboxtest.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="inboxtest">',
'ispmonitor': '<img src="/assets/logo-ispmonitor.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="ispmonitor">',
'presentationai': '<img src="/assets/logo-presentationai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="presentationai">',
'dashboardai': '<img src="/assets/logo-dashboardai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="dashboardai">',
'translateai': '<img src="/assets/logo-translateai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="translateai">',
'bizplan': '<img src="/assets/logo-bizplan.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="bizplan">',
'contractai': '<img src="/assets/logo-contractai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="contractai">',
'meeting-summary': '<img src="/assets/logo-meeting-summary.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="meeting-summary">',
'cloudbridge': '<img src="/assets/logo-cloudbridge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="cloudbridge">',
'weval-mind': '<img src="/assets/logo-weval-mind.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="weval-mind">',
'blacklistguard': '<img src="/assets/logo-blacklistguard.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="blacklistguard">',
'emailverify': '<img src="/assets/logo-emailverify.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="emailverify">',
'creative-factory': '<img src="/assets/logo-creative-factory.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="creative-factory">',
'copyai': '<img src="/assets/logo-copyai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="copyai">',
'reputationai': '<img src="/assets/logo-reputationai.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="reputationai">',
'mailforge': '<img src="/assets/logo-mailforge.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="MailForge">',
'scout': '<img src="/assets/logo-scout.svg" style="width:36px;height:36px;border-radius:6px;object-fit:contain" alt="scout">'
};
function ProductCard({ product: p, onSubscribe, setPage }) {
const catColor = p.is_flagship ? 'var(--g)' : colors[p.category] || 'var(--t)';
const url = PRODUCT_URLS[p.slug];
return React.createElement('div', {
style:{...css.card, position:'relative',overflow:'hidden'},
onMouseEnter: e => { e.currentTarget.style.borderColor='rgba(0,201,167,0.25)'; e.currentTarget.style.transform='translateY(-3px)'; },
onMouseLeave: e => { e.currentTarget.style.borderColor='var(--border)'; e.currentTarget.style.transform='none'; },
},
// Top bar
React.createElement('div', { style:{position:'absolute',top:0,left:0,right:0,height:'3px',background:`linear-gradient(90deg,${catColor},var(--p))` } }),
// Header
React.createElement('div', { style:{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:'0.6rem'} },
(p.icon&&p.icon.startsWith('/'))?React.createElement('img',{src:p.icon,style:{width:'36px',height:'36px',borderRadius:'8px',objectFit:'contain',background:'#fff',padding:'4px'},alt:p.name}):((p.icon||'').indexOf('/assets/')===0?React.createElement('img',{src:p.icon,style:{width:36,height:36,borderRadius:6,objectFit:'contain',background:'#fff',padding:4},alt:p.name||''}):((p.icon||'').indexOf('/assets/')===0?React.createElement('img',{src:p.icon,style:{width:36,height:36,borderRadius:6,objectFit:'contain',background:'#fff',padding:4},alt:p.name||''}):React.createElement('span',{style:{fontSize:'1.8rem'}},p.icon||'?'))),
React.createElement('div', { style:{display:'flex',gap:'0.4rem',alignItems:'center'} },
p.is_live && React.createElement('span', { style:{...css.badge,background:'rgba(0,201,167,0.15)',color:'var(--t)'} }, 'LIVE'),
p.is_flagship && React.createElement('span', { style:{...css.badge,background:'rgba(240,198,116,0.15)',color:'var(--g)'} }, 'FLAGSHIP'),
)
),
React.createElement('div', { style:{fontSize:'1.05rem',fontWeight:700,marginBottom:'0.12rem'} }, p.name),
React.createElement('div', { style:{fontSize:'0.72rem',color:catColor,fontWeight:500,marginBottom:'0.5rem'} }, p.tagline),
p.pricing_free && React.createElement('div', { style:{fontSize:'0.7rem',color:'var(--si)',marginBottom:'0.4rem'} }, '🆓 ', p.pricing_free),
p.pricing_pro && React.createElement('div', { style:{fontSize:'0.7rem',color:'var(--si)',marginBottom:'0.6rem'} }, '💎 ', p.pricing_pro),
// Actions
React.createElement('div', { style:{display:'flex',gap:'0.5rem',marginTop:'0.8rem',borderTop:'1px solid rgba(255,255,255,0.04)',paddingTop:'0.8rem'} },
React.createElement('button', {
style:{...css.btn,...css.btnT,flex:1,fontSize:'0.75rem',padding:'0.45rem'},
onClick: () => onSubscribe(p.slug)
}, '+ S\'abonner'),
url && React.createElement('button', {
style:{...css.btn,...css.btnO,fontSize:'0.75rem',padding:'0.45rem'},
onClick: () => window.open(url, '_blank')
}, 'Ouvrir →'),
),
false
);
}
function DashboardPage() {
const { apiFetch } = useAuth();
const [data, setData] = useState(null);
useEffect(() => { apiFetch('dashboard').then(d => d.ok && setData(d.dashboard)); }, []);
if (!data) return React.createElement('div', { style:{color:'var(--si)',padding:'3rem',textAlign:'center'} }, 'Chargement...');
const cards = [
{ label:'Produits actifs', value:data.active_subscriptions, icon:'📦', color:'var(--t)' },
{ label:'Clés API', value:data.active_keys, icon:'🔑', color:'var(--p)' },
{ label:'Requêtes (30j)', value:data.usage_30d?.requests || 0, icon:'📊', color:'var(--bl)' },
{ label:'Produits dispo', value:data.products_available, icon:'🏪', color:'var(--g)' },
];
return React.createElement('div', null,
React.createElement('h1', { style:{fontSize:'1.8rem',fontWeight:800,marginBottom:'0.4rem'} }, '📊 Dashboard'),
React.createElement('p', { style:{color:'var(--si)',marginBottom:'2rem',fontSize:'0.9rem'} }, 'Vue d\'ensemble de votre utilisation'),
React.createElement('div', { style:{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:'1rem',marginBottom:'2rem'} },
cards.map((c,i) => React.createElement('div', { key:i, style:{...css.card,textAlign:'center'} },
React.createElement('div', { style:{fontSize:'1.5rem',marginBottom:'0.4rem'} }, c.icon),
React.createElement('div', { style:{fontFamily:'Space Mono',fontSize:'1.8rem',fontWeight:700,color:c.color} }, c.value),
React.createElement('div', { style:{fontSize:'0.75rem',color:'var(--si)',marginTop:'0.2rem'} }, c.label),
))
),
// Recent activity
React.createElement('div', { style:css.card },
React.createElement('h3', { style:{fontSize:'1rem',fontWeight:600,marginBottom:'1rem'} }, 'Activité récente'),
data.recent_activity?.length > 0
? data.recent_activity.map((a,i) => React.createElement('div', { key:i, style:{display:'flex',justifyContent:'space-between',padding:'0.5rem 0',borderBottom:'1px solid rgba(255,255,255,0.03)',fontSize:'0.82rem'} },
React.createElement('span', null, a.product_slug, ' — ', a.action),
React.createElement('span', { style:{color:'var(--si)',fontSize:'0.75rem'} }, new Date(a.created_at).toLocaleString('fr-FR')),
))
: React.createElement('p', { style:{color:'var(--si)',fontSize:'0.85rem'} }, 'Aucune activité. Abonnez-vous à un produit pour commencer.')
)
);
}
function SubscriptionsPage({ setPage }) {
const { apiFetch } = useAuth();
const [subs, setSubs] = useState([]);
useEffect(() => { apiFetch('subscriptions').then(d => d.ok && setSubs(d.subscriptions)); }, []);
return React.createElement('div', null,
React.createElement('h1', { style:{fontSize:'1.8rem',fontWeight:800,marginBottom:'0.4rem'} }, '📦 Mes Produits'),
React.createElement('p', { style:{color:'var(--si)',marginBottom:'2rem',fontSize:'0.9rem'} }, `${subs.length} abonnement(s) actif(s)`),
subs.length === 0
? React.createElement('div', { style:{...css.card,textAlign:'center',padding:'3rem'} },
React.createElement('div', { style:{fontSize:'3rem',marginBottom:'1rem',opacity:0.3} }, '📦'),
React.createElement('p', { style:{color:'var(--si)'} }, 'Aucun abonnement.'),
React.createElement('button', { style:{...css.btn,...css.btnT,marginTop:'1rem'}, onClick:()=>setPage('store') }, 'Explorer l\'AppStore →')
)
: React.createElement('div', { style:{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(300px,1fr))',gap:'1rem'} },
subs.map(s => {
const url = PRODUCT_URLS[s.slug] || s.url_app;
return React.createElement('div', { key:s.id, style:css.card },
React.createElement('div', { style:{display:'flex',alignItems:'center',gap:'0.8rem',marginBottom:'0.8rem'} },
((s.icon||'').indexOf('/assets/')===0?React.createElement('img',{src:s.icon,style:{width:36,height:36,borderRadius:6,objectFit:'contain',background:'#fff',padding:4},alt:''}):React.createElement('span',{style:{fontSize:'1.5rem'}},s.icon||'')),
React.createElement('div', null,
React.createElement('div', { style:{fontWeight:700} }, s.product_name),
React.createElement('div', { style:{fontSize:'0.75rem',color:'var(--t)'} }, s.tagline),
)
),
React.createElement('div', { style:{display:'flex',gap:'0.4rem',fontSize:'0.7rem',marginBottom:'0.8rem'} },
React.createElement('span', { style:{...css.badge,background:'var(--t15)',color:'var(--t)'} }, s.plan.toUpperCase()),
React.createElement('span', { style:{...css.badge,background:'rgba(0,201,167,0.1)',color:'var(--t)'} }, s.status),
),
React.createElement('div', { style:{display:'flex',gap:'0.5rem'} },
url && React.createElement('button', { style:{...css.btn,...css.btnT,flex:1,fontSize:'0.75rem'}, onClick:()=>window.open(url,'_blank') }, 'Ouvrir →'),
s.api_endpoint && React.createElement('button', { style:{...css.btn,...css.btnO,fontSize:'0.75rem'}, onClick:()=>setPage('keys') }, '🔑 API Key'),
)
);
})
)
);
}
function KeysPage() {
const { apiFetch } = useAuth();
const [keys, setKeys] = useState([]);
const [copied, setCopied] = useState(null);
useEffect(() => { apiFetch('keys').then(d => d.ok && setKeys(d.keys)); }, []);
const copy = (key, id) => { navigator.clipboard?.writeText(key); setCopied(id); setTimeout(()=>setCopied(null),2000); };
const revoke = async (id) => {
if (!confirm('Révoquer cette clé ?')) return;
await apiFetch('keys/revoke', { method:'POST', body:JSON.stringify({key_id:id}) });
setKeys(keys.filter(k => k.id !== id));
};
return React.createElement('div', null,
React.createElement('h1', { style:{fontSize:'1.8rem',fontWeight:800,marginBottom:'2rem'} }, '🔑 API Keys'),
keys.length === 0
? React.createElement('div', { style:{...css.card,textAlign:'center',padding:'3rem'} },
React.createElement('p', { style:{color:'var(--si)'} }, 'Aucune clé API. Abonnez-vous à un produit avec API pour en obtenir une.'))
: keys.map(k => React.createElement('div', { key:k.id, style:{...css.card,marginBottom:'0.8rem',display:'flex',justifyContent:'space-between',alignItems:'center'} },
React.createElement('div', null,
React.createElement('div', { style:{fontWeight:600,fontSize:'0.9rem'} }, k.name),
React.createElement('div', { style:{fontFamily:'Space Mono',fontSize:'0.78rem',color:'var(--t)',marginTop:'0.2rem'} }, k.api_key_masked),
React.createElement('div', { style:{fontSize:'0.7rem',color:'var(--si)',marginTop:'0.2rem'} }, k.product_name, ' · Rate limit: ', k.rate_limit, '/h'),
),
React.createElement('div', { style:{display:'flex',gap:'0.4rem'} },
React.createElement('button', { style:{...css.btn,...css.btnT,fontSize:'0.72rem',padding:'0.35rem 0.6rem'}, onClick:()=>copy(k.api_key_full, k.id) }, copied===k.id ? '✅' : '📋 Copy'),
k.is_active && React.createElement('button', { style:{...css.btn,background:'rgba(255,107,107,0.15)',color:'var(--co)',fontSize:'0.72rem',padding:'0.35rem 0.6rem'}, onClick:()=>revoke(k.id) }, 'Révoquer'),
)
))
);
}
function UsagePage() {
const { apiFetch } = useAuth();
const [usage, setUsage] = useState([]);
useEffect(() => { apiFetch('usage?days=30').then(d => d.ok && setUsage(d.usage)); }, []);
return React.createElement('div', null,
React.createElement('h1', { style:{fontSize:'1.8rem',fontWeight:800,marginBottom:'2rem'} }, '📈 Usage (30 jours)'),
usage.length === 0
? React.createElement('div', { style:{...css.card,textAlign:'center',padding:'3rem'} },
React.createElement('p', { style:{color:'var(--si)'} }, 'Aucune utilisation enregistrée.'))
: React.createElement('table', { style:{width:'100%',borderCollapse:'collapse'} },
React.createElement('thead', null,
React.createElement('tr', null,
['Produit','Action','Requêtes','Tokens','Coût'].map(h =>
React.createElement('th', { key:h, style:{padding:'0.7rem',textAlign:'left',fontSize:'0.72rem',textTransform:'uppercase',letterSpacing:'0.05em',color:'var(--t)',borderBottom:'2px solid rgba(0,201,167,0.15)',background:'var(--dk)'} }, h)
)
)
),
React.createElement('tbody', null,
usage.map((u,i) => React.createElement('tr', { key:i },
[u.product_slug, u.action, u.count, u.tokens||0, `${parseFloat(u.cost||0).toFixed(2)}$`].map((v,j) =>
React.createElement('td', { key:j, style:{padding:'0.6rem 0.7rem',borderBottom:'1px solid rgba(255,255,255,0.03)',fontSize:'0.85rem',fontFamily: j>1 ? 'Space Mono' : 'inherit'} }, v)
)
))
)
)
);
}
function AdminPage() {
const { apiFetch } = useAuth();
const [stats, setStats] = useState(null);
const [users, setUsers] = useState([]);
useEffect(() => {
apiFetch('admin/stats').then(d => d.ok && setStats(d.stats));
apiFetch('admin/users').then(d => d.ok && setUsers(d.users));
}, []);
if (!stats) return React.createElement('div', { style:{color:'var(--si)',padding:'3rem'} }, 'Chargement...');
return React.createElement('div', null,
React.createElement('h1', { style:{fontSize:'1.8rem',fontWeight:800,marginBottom:'2rem'} }, '⚙️ Admin'),
React.createElement('div', { style:{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:'1rem',marginBottom:'2rem'} },
[
{l:'Utilisateurs',v:stats.total_users,c:'var(--t)'},
{l:'Abonnements actifs',v:stats.total_subscriptions,c:'var(--p)'},
{l:'Clés API',v:stats.total_api_keys,c:'var(--bl)'},
{l:'Requêtes 30j',v:stats.requests_30d,c:'var(--g)'},
{l:'Revenue 30j',v:`${stats.revenue_30d.toFixed(2)}$`,c:'var(--or)'},
{l:'Actifs 7j',v:stats.active_users_7d,c:'var(--t)'},
].map((s,i) => React.createElement('div', { key:i, style:{...css.card,textAlign:'center'} },
React.createElement('div', { style:{fontFamily:'Space Mono',fontSize:'1.5rem',fontWeight:700,color:s.c} }, s.v),
React.createElement('div', { style:{fontSize:'0.75rem',color:'var(--si)'} }, s.l),
))
),
React.createElement('div', { style:css.card },
React.createElement('h3', { style:{marginBottom:'1rem',fontWeight:600} }, `Utilisateurs (${users.length})`),
React.createElement('table', { style:{width:'100%',borderCollapse:'collapse',fontSize:'0.82rem'} },
React.createElement('thead', null, React.createElement('tr', null,
['Email','Nom','Entreprise','Plan','Subs','Inscrit'].map(h =>
React.createElement('th', { key:h, style:{padding:'0.5rem',textAlign:'left',color:'var(--t)',borderBottom:'1px solid rgba(0,201,167,0.1)',fontSize:'0.7rem',textTransform:'uppercase'} }, h))
)),
React.createElement('tbody', null,
users.map(u => React.createElement('tr', { key:u.id },
[u.email, u.name, u.company, u.plan, u.subs, new Date(u.created_at).toLocaleDateString('fr-FR')].map((v,j) =>
React.createElement('td', { key:j, style:{padding:'0.45rem 0.5rem',borderBottom:'1px solid rgba(255,255,255,0.02)'} }, v || '—'))
))
)
)
)
);
}
function AuthPage({ mode, setPage }) {
const { login, register } = useAuth();
const [form, setForm] = useState({ email:'',password:'',name:'',company:'',phone:'' });
const [err, setErr] = useState('');
const [loading, setLoading] = useState(false);
const handle = async () => {
setErr(''); setLoading(true);
try {
if (mode === 'login') await login(form.email, form.password);
else await register(form);
setPage('dashboard');
} catch(e) { setErr(e.message); }
setLoading(false);
};
return React.createElement('div', { style:{maxWidth:'400px',margin:'0 auto',paddingTop:'calc(52px + 4rem)'} },
React.createElement('div', { style:{...css.card,padding:'2.5rem'} },
React.createElement('h2', { style:{fontSize:'1.4rem',fontWeight:700,marginBottom:'0.4rem',textAlign:'center'} },
mode === 'login' ? 'Connexion' : 'Créer un compte'
),
React.createElement('p', { style:{color:'var(--si)',fontSize:'0.85rem',textAlign:'center',marginBottom:'2rem'} },
mode === 'login' ? 'Accédez à vos produits' : 'Accès gratuit à tous les produits'
),
err && React.createElement('div', { style:{background:'rgba(255,107,107,0.1)',border:'1px solid rgba(255,107,107,0.2)',borderRadius:'8px',padding:'0.6rem',marginBottom:'1rem',fontSize:'0.82rem',color:'var(--co)'} }, err),
mode === 'register' && [
React.createElement('div', { key:'n', style:{marginBottom:'1rem'} },
React.createElement('label', { style:css.label }, 'Nom complet'),
React.createElement('input', { style:css.input, value:form.name, onChange:e=>setForm({...form,name:e.target.value}), placeholder:'Votre nom' })
),
React.createElement('div', { key:'c', style:{marginBottom:'1rem'} },
React.createElement('label', { style:css.label }, 'Entreprise'),
React.createElement('input', { style:css.input, value:form.company, onChange:e=>setForm({...form,company:e.target.value}), placeholder:'Nom entreprise' })
),
],
React.createElement('div', { style:{marginBottom:'1rem'} },
React.createElement('label', { style:css.label }, 'Email'),
React.createElement('input', { style:css.input, type:'email', value:form.email, onChange:e=>setForm({...form,email:e.target.value}), placeholder:'votre@email.com' })
),
React.createElement('div', { style:{marginBottom:'1.5rem'} },
React.createElement('label', { style:css.label }, 'Mot de passe'),
React.createElement('input', { style:css.input, type:'password', value:form.password, onChange:e=>setForm({...form,password:e.target.value}), placeholder:'6 caractères minimum', onKeyDown:e=>e.key==='Enter'&&handle() })
),
React.createElement('button', { style:{...css.btn,...css.btnT,width:'100%',padding:'0.8rem',fontSize:'0.95rem'}, onClick:handle, disabled:loading },
loading ? '...' : mode === 'login' ? 'Se connecter →' : 'Créer mon compte →'
),
React.createElement('p', { style:{textAlign:'center',marginTop:'1.2rem',fontSize:'0.82rem',color:'var(--si)'} },
mode === 'login' ? 'Pas de compte ? ' : 'Déjà inscrit ? ',
React.createElement('span', { style:{color:'var(--t)',cursor:'pointer',fontWeight:600}, onClick:()=>setPage(mode==='login'?'register':'login') },
mode === 'login' ? 'Créer un compte' : 'Se connecter'
)
)
)
);
}
// ═══ APP ═══
function App() {
const [page, setPage] = useState('store');
const { user, loading } = useAuth();
if (loading) return React.createElement('div', { style:{display:'flex',alignItems:'center',justifyContent:'center',minHeight:'100vh',color:'var(--si)'} }, 'Chargement...');
const authPages = ['dashboard','subscriptions','keys','usage','admin'];
const currentPage = (authPages.includes(page) && !user) ? 'login' : page;
let content;
switch (currentPage) {
case 'store': content = React.createElement(StorePage, { setPage }); break;
case 'dashboard': content = React.createElement(DashboardPage); break;
case 'subscriptions': content = React.createElement(SubscriptionsPage, { setPage }); break;
case 'keys': content = React.createElement(KeysPage); break;
case 'usage': content = React.createElement(UsagePage); break;
case 'admin': content = React.createElement(AdminPage); break;
case 'login': content = React.createElement(AuthPage, { mode:'login', setPage }); break;
case 'register': content = React.createElement(AuthPage, { mode:'register', setPage }); break;
default: content = React.createElement(StorePage, { setPage });
}
const showSidebar = !['login','register'].includes(currentPage);
return React.createElement('div', null,
React.createElement(Nav, { page: currentPage, setPage }),
showSidebar && React.createElement(SideBar, { page: currentPage, setPage }),
React.createElement('div', { style: showSidebar ? css.main : { padding:'2rem 3%' } }, content)
);
}
// ═══ RENDER ═══
ReactDOM.render(
React.createElement(AuthProvider, null, React.createElement(App)),
document.getElementById('root')
);
</script>
<script>
// Fallback: if chatbot didn't load after 3s, log error
setTimeout(function(){
if(!document.getElementById('weval-bot-widget')){
console.warn('WEVAL chatbot: external JS failed to load, retrying...');
var s=document.createElement('script');
s.src='/weval-chatbot-v3.js
<!-- WEVAL Product Assistant Chatbot Widget --><div id="weval-bot-widget" style="position:fixed;bottom:20px;right:20px;z-index:9999;font-family:'Inter',system-ui,sans-serif"><style>#weval-bot-btn{width:56px;height:56px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#8b5cf6);border:none;cursor:pointer;box-shadow:0 4px 24px rgba(99,102,241,.4);display:flex;align-items:center;justify-content:center;transition:transform .2s,box-shadow .2s}#weval-bot-btn:hover{transform:scale(1.08);box-shadow:0 6px 32px rgba(99,102,241,.5)}#weval-bot-btn svg{width:28px;height:28px;fill:#fff}#weval-bot-badge{position:absolute;top:-2px;right:-2px;width:14px;height:14px;background:#22c55e;border-radius:50%;border:2px solid #fff;animation:pulse-badge 2s infinite}@keyframes pulse-badge{0%,100%{opacity:1}50%{opacity:.5}}#weval-bot-panel{display:none;position:fixed;bottom:90px;right:20px;width:380px;max-height:520px;background:#0f1629;border:1px solid rgba(99,102,241,.2);border-radius:16px;box-shadow:0 12px 48px rgba(0,0,0,.5);overflow:hidden;flex-direction:column}#weval-bot-panel.open{display:flex}#weval-bot-head{background:linear-gradient(135deg,#6366f1,#8b5cf6);padding:14px 18px;display:flex;align-items:center;gap:10px}#weval-bot-head .avatar{width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;font-size:18px}#weval-bot-head .info{flex:1;color:#fff}#weval-bot-head .info .name{font-weight:600;font-size:14px}#weval-bot-head .info .status{font-size:11px;opacity:.8}#weval-bot-close{background:none;border:none;color:rgba(255,255,255,.7);cursor:pointer;font-size:20px;padding:4px}#weval-bot-msgs{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px;min-height:280px;max-height:360px}.bot-msg{max-width:85%;padding:10px 14px;border-radius:12px;font-size:13px;line-height:1.5;word-wrap:break-word}.bot-msg.bot{background:rgba(99,102,241,.12);color:#e2e8f0;border-bottom-left-radius:4px;align-self:flex-start}.bot-msg.user{background:#6366f1;color:#fff;border-bottom-right-radius:4px;align-self:flex-end}.bot-msg a{color:#818cf8;text-decoration:underline}.bot-typing{display:flex;gap:4px;padding:10px 14px;align-self:flex-start}.bot-typing span{width:6px;height:6px;background:#6366f1;border-radius:50%;animation:bounce .6s infinite alternate}.bot-typing span:nth-child(2){animation-delay:.2s}.bot-typing span:nth-child(3){animation-delay:.4s}@keyframes bounce{to{opacity:.3;transform:translateY(-4px)}}#weval-bot-input-area{padding:10px 14px;border-top:1px solid rgba(255,255,255,.06);display:flex;gap:8px}#weval-bot-input{flex:1;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:8px 12px;color:#e2e8f0;font-size:13px;outline:none}#weval-bot-input::placeholder{color:rgba(255,255,255,.3)}#weval-bot-send{background:#6366f1;border:none;border-radius:8px;padding:8px 12px;color:#fff;cursor:pointer;font-size:13px;font-weight:600}#weval-bot-send:hover{background:#5558e6}.quick-btns{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.quick-btn{background:rgba(99,102,241,.15);border:1px solid rgba(99,102,241,.25);color:#a5b4fc;padding:5px 10px;border-radius:6px;font-size:11px;cursor:pointer;transition:all .15s}.quick-btn:hover{background:rgba(99,102,241,.3);color:#fff}</style><button id="weval-bot-btn" onclick="toggleBot()"> <svg viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg> <div id="weval-bot-badge"></div></button><div id="weval-bot-panel"> <div id="weval-bot-head"> <div class="avatar">W</div> <div class="info"> <div class="name">WEVAL Assistant</div> <div class="status">En ligne - 44 produits</div> </div> <button id="weval-bot-close" onclick="toggleBot()">&times;</button> </div> <div id="weval-bot-msgs"></div> <div id="weval-bot-input-area"> <input id="weval-bot-input" placeholder="Posez une question sur nos produits..." onkeypress="if(event.key==='Enter')sendBot()"> <button id="weval-bot-send" onclick="sendBot()">Envoyer</button> </div></div><script>const PRODUCTS_KB = { deliVerscore: {name:'DeliverScore',desc:'Audit deliverability email - SPF/DKIM/DMARC/blacklists. Score + recommandations IA.',price:'Gratuit + Pro 49$/mo',url:'/products/deliverscore.html',category:'Email Intelligence'}, medreach: {name:'MedReach API',desc:'Base de 18596 medecins verifies au Maghreb. API REST + export.',price:'Gratuit + Pro 300$/mo',url:'/products/medreach.html',category:'Data'}, gpu: {name:'WEVIA Inference',desc:'IA Souveraine WEVIA sur GPU RTX 4000 Ada. API OpenAI-compatible.',price:'Gratuit + Pro 100$/mo',url:'/products/gpu-inference.html',category:'IA'}, contentfactory: {name:'AI Content Factory',desc:'Génération de contenu IA - articles, fiches produits, LinkedIn. 6 templates.',price:'Gratuit + Pro 30$/mo',url:'/products/content-factory.html',category:'IA'}, proposalai: {name:'ProposalAI',desc:'Generateur de propositions commerciales qualite Big4. Brief -> propale en 30 sec.',price:'Gratuit + Pro 20$/mo',url:'/products/proposalai.html',category:'IA'}, blueprintai: {name:'BlueprintAI',desc:'Process docs, BPMN, CDC, blueprints L1/L2/L3, 8D, RACI.',price:'25$/mo',url:'/products/blueprintai.html',category:'IA'}, storeforge: {name:'StoreForge',desc:'E-commerce IA. Boutique en 5 min. Descriptions IA, chatbot WEVIA, paiement Maroc.',price:'0-299 MAD/mois',url:'/products/storeforge.html',category:'Commerce'}, leadforge: {name:'LeadForge',desc:'B2B Lead Intelligence. Leads qualifies et verifies tous secteurs.',price:'Sur devis',url:'/products/workspace.html',category:'Data'}, mailwarm: {name:'MailWarm',desc:'Email Reputation Builder. Ameliorez votre délivrabilité avec 500+ partenaires.',price:'30$/compte/mo',url:'/products/workspace.html',category:'Email'}, sentinel: {name:'Sentinel Monitor',desc:'Cyber monitoring PME - SSL, ports, DNS, OWASP. Alertes Telegram.',price:'Gratuit + Pro 50$/mo',url:'/products/workspace.html',category:'Security'}, outreachai: {name:'OutreachAI',desc:'Smart Email Engagement IA. Sequences personnalisées, tracking, analytics.',price:'200$/mo',url:'/products/workspace.html',category:'Email'}, wevia: {name:'WEVIA White-Label',desc:'Chatbot IA clé en main. Widget embed 5 min. KB custom, memoire, vision.',price:'100-300$/mo',url:'/products/wevia-whitelabel.html',category:'IA'}, emailverify: {name:'EmailVerify',desc:'Validation email temps reel MX+SMTP+disposable.',price:'49$/mo',url:'/products/workspace.html',category:'Email Intelligence'}, blacklistguard: {name:'BlacklistGuard',desc:'Monitoring 100+ blacklists RBL + alertes.',price:'29$/mo',url:'/products/workspace.html',category:'Email Intelligence'}, reputationai: {name:'ReputationAI',desc:'Score reputation domaine + historique.',price:'39$/mo',url:'/products/workspace.html',category:'Email Intelligence'}, copyai: {name:'CopyAI WEVAL',desc:'Copywriting IA - emails, sujets, CTA. Cloud souverain.',price:'39$/mo',url:'/products/workspace.html',category:'IA'}, dataharvest: {name:'DataHarvest',desc:'Web Data Enrichment et Intelligence. Sources publiques structurées.',price:'99$/mo',url:'/products/workspace.html',category:'Data'}, smsforge: {name:'SMSForge',desc:'SMS Marketing et Notifications opt-in Maghreb/Europe.',price:'49$/mo',url:'/products/workspace.html',category:'Marketing'}, adscontrol: {name:'AdsControl',desc:'Multi-channel ads FB/Google/LinkedIn/TikTok.',price:'99$/mo',url:'/products/workspace.html',category:'Marketing'}, wevalcrm: {name:'WEVAL CRM',desc:'CRM leger pipeline + contacts + IA.',price:'Gratuit + Pro 29$/mo',url:'/products/workspace.html',category:'Business'}, canvasai: {name:'CanvasAI',desc:'Design IA - visuels, bannieres, logos.',price:'29$/mo',url:'/products/workspace.html',category:'IA'}, devforge: {name:'DevForge AI',desc:'12 modules dev: specs, tests, code gen, API design, security review.',price:'199$/mo',url:'/products/workspace.html',category:'Dev'}, ethica: {name:'MedReach HCP',desc:'Data 18596 medecins verifies Maghreb + campagnes pharma HCP.',price:'300$/mo',url:'/products/workspace.html',category:'Data'}, arsenal: {name:'Arsenal Framework',desc:'150+ ecrans. ERP Intelligence, Brain Engine, PMTA. Enterprise.',price:'2000-10000$/mo',url:'/products/arsenal.html',category:'Flagship'}, wevads: {name:'WEVADS Platform',desc:'Infrastructure email complete. Brain Engine 646 configs. 6.65M contacts.',price:'Enterprise',url:'/products/wevads.html',category:'Flagship'},};function getProductList() { return Object.values(PRODUCTS_KB).map(p => `<b>${p.name}</b> - ${p.desc} (<a href="${p.url}">${p.price}</a>)`).join('<br><br>');}function findProducts(query) { const q = query.toLowerCase(); const matches = []; const keywords = { email: ['deliVerscore','emailverify','blacklistguard','reputationai','mailwarm','outreachai'], ia: ['gpu','contentfactory','proposalai','blueprintai','copyai','canvasai','devforge','wevia'], data: ['medreach','leadforge','dataharvest','ethica'], security: ['sentinel','blacklistguard'], ecommerce: ['storeforge'], marketing: ['smsforge','adscontrol','outreachai'], crm: ['wevalcrm'], sap: ['arsenal'], enterprise: ['arsenal','wevads','wevia'], gratuit: [], prix: [], maroc: ['medreach','ethica','smsforge','storeforge'], }; for (const [kw, ids] of Object.entries(keywords)) { if (q.includes(kw)) ids.forEach(id => { if (!matches.includes(id)) matches.push(id); }); } // Also search in product names and descriptions for (const [id, p] of Object.entries(PRODUCTS_KB)) { if (p.name.toLowerCase().includes(q) || p.desc.toLowerCase().includes(q) || p.category.toLowerCase().includes(q)) { if (!matches.includes(id)) matches.push(id); } } return matches.map(id => PRODUCTS_KB[id]).filter(Boolean);}function botReply(userMsg) { const q = userMsg.toLowerCase(); // Greetings if (/^(bonjour|salut|hello|hi|hey|coucou)/.test(q)) { return `Bonjour ! Je suis l'assistant WEVAL. Nous avons <b>44 produits SaaS</b> en production. Comment puis-je vous aider ?<div class="quick-btns"><span class="quick-btn" onclick="askBot('Quels sont vos produits?')">Voir les produits</span><span class="quick-btn" onclick="askBot('email deliverability')">Email</span><span class="quick-btn" onclick="askBot('intelligence artificielle')">IA</span><span class="quick-btn" onclick="askBot('prix')">Tarifs</span></div>`; } // List all if (/tous|tout|liste|produit|service|catalogue|quoi/.test(q)) { const cats = {}; Object.values(PRODUCTS_KB).forEach(p => { if (!cats[p.category]) cats[p.category]=[]; cats[p.category].push(p); }); let html = `Voici nos <b>${Object.keys(PRODUCTS_KB).length} produits</b> par categorie :<br><br>`; for (const [cat, prods] of Object.entries(cats)) { html += `<b>${cat}</b><br>`; prods.forEach(p => { html += `&bull; <a href="${p.url}">${p.name}</a> - ${p.price}<br>`; }); html += '<br>'; } html += `<div class="quick-btns"><span class="quick-btn" onclick="askBot('email')">Email</span><span class="quick-btn" onclick="askBot('IA')">IA</span><span class="quick-btn" onclick="askBot('essayer gratuit')">Gratuit</span></div>`; return html; } // Pricing if (/prix|tarif|cout|combien|pricing|gratuit|free/.test(q)) { const free = Object.values(PRODUCTS_KB).filter(p => p.price.toLowerCase().includes('gratuit')); let html = `<b>Produits avec plan gratuit (${free.length}):</b><br>`; free.forEach(p => { html += `&bull; <a href="${p.url}">${p.name}</a> - ${p.price}<br>`; }); html += `<br>Tous les prix sont sur <a href="/products/">notre catalogue</a>. Besoin d'un devis personnalisé ?<div class="quick-btns"><span class="quick-btn" onclick="askBot('demo')">Demander une demo</span><span class="quick-btn" onclick="window.location.href='/contact-us'">Nous contacter</span></div>`; return html; } // Demo / essai if (/demo|essai|tester|try|commencer|start/.test(q)) { return `Pour commencer gratuitement :<br><br>1. <a href="/products/workspace.html">Ouvrir le Workspace</a> et créer un compte<br>2. Vous aurez acces a tous les produits gratuits<br>3. Testez DeliverScore, WEVIA Inference, Content Factory...<br><br>Ou <a href="/contact-us">contactez-nous</a> pour une demo personnalisée !<div class="quick-btns"><span class="quick-btn" onclick="window.location.href='/products/workspace.html'">Ouvrir Workspace</span></div>`; } // Specific product search const matches = findProducts(q); if (matches.length > 0) { let html = `J'ai trouve <b>${matches.length} produit(s)</b> correspondant :<br><br>`; matches.slice(0, 6).forEach(p => { html += `<b><a href="${p.url}">${p.name}</a></b><br>${p.desc}<br><i>${p.price}</i><br><br>`; }); if (matches.length > 6) html += `...et ${matches.length - 6} autres.<br>`; html += `<div class="quick-btns"><span class="quick-btn" onclick="window.location.href='${matches[0].url}'">Voir ${matches[0].name}</span><span class="quick-btn" onclick="askBot('prix')">Tarifs</span></div>`; return html; } // Default - suggest contacting or browsing return `Je n'ai pas trouve de produit specifique pour "${userMsg}". Nos 44 produits couvrent : Email Intelligence, IA/GPU, Data, Security, Marketing, E-commerce et Dev.<br><br><div class="quick-btns"><span class="quick-btn" onclick="askBot('tous les produits')">Voir tout</span><span class="quick-btn" onclick="window.location.href='/contact-us'">Contacter WEVAL</span><span class="quick-btn" onclick="window.location.href='/products/workspace.html'">Workspace</span></div>`;}function toggleBot() { const p = document.getElementById('weval-bot-panel'); p.classList.toggle('open'); if (p.classList.contains('open') && document.getElementById('weval-bot-msgs').children.length === 0) { addMsg('bot', `Bonjour ! Je suis l'assistant produits WEVAL. Nous proposons <b>44 SaaS</b> en production. Que recherchez-vous ?<div class="quick-btns"><span class="quick-btn" onclick="askBot('Tous les produits')">Catalogue</span><span class="quick-btn" onclick="askBot('email')">Email</span><span class="quick-btn" onclick="askBot('intelligence artificielle')">IA</span><span class="quick-btn" onclick="askBot('gratuit')">Gratuit</span><span class="quick-btn" onclick="askBot('demo')">Demo</span></div>`); } document.getElementById('weval-bot-badge').style.display = 'none';}function addMsg(type, html) { const msgs = document.getElementById('weval-bot-msgs'); const div = document.createElement('div'); div.className = 'bot-msg ' + type; div.innerHTML = html; msgs.appendChild(div); msgs.scrollTop = msgs.scrollHeight;}function askBot(q) { document.getElementById('weval-bot-input').value = q; sendBot();}function sendBot() { const input = document.getElementById('weval-bot-input'); const msg = input.value.trim(); if (!msg) return; input.value = ''; addMsg('user', msg); // Show typing const msgs = document.getElementById('weval-bot-msgs'); const typing = document.createElement('div'); typing.className = 'bot-typing'; typing.innerHTML = '<span></span><span></span><span></span>'; msgs.appendChild(typing); msgs.scrollTop = msgs.scrollHeight; setTimeout(() => { typing.remove(); addMsg('bot', botReply(msg)); }, 400 + Math.random() * 600);}</script></div>
<script src="/wbot.js"></script>
</body>
</html>