108 lines
9.0 KiB
PHP
108 lines
9.0 KiB
PHP
<?php
|
|
function weviaHostArtifact($response) {
|
|
if (preg_match('/```(?:jsx|js|javascript|react)\s*\n([\s\S]*?)```/', $response, $m)) {
|
|
$jsxCode = $m[1];
|
|
|
|
// Strip ALL imports
|
|
$jsxCode = preg_replace('/^\s*import\s+.+?from\s+[\x27"].+?[\x27"];?\s*$/m', '', $jsxCode);
|
|
// Strip export statements (Babel compiles to CommonJS which fails in browser)
|
|
$jsxCode = preg_replace('/^\s*export\s+default\s+/m', '/* exported */ window.__COMP__ = ', $jsxCode);
|
|
$jsxCode = preg_replace('/^\s*export\s+/m', '/* export */ ', $jsxCode);
|
|
|
|
// React globals + simple chart components (replace Recharts)
|
|
$globals = <<<'JS'
|
|
const {useState,useEffect,useRef,useCallback,useMemo,Fragment,createElement}=React;
|
|
|
|
// Simple CSS Bar Chart component (replaces Recharts)
|
|
function SimpleChart({data, dataKey, xKey, title, color}) {
|
|
const max = Math.max(...data.map(d => d[dataKey] || 0));
|
|
return React.createElement('div', {style:{margin:'20px 0'}},
|
|
title && React.createElement('h3', {style:{marginBottom:'12px',color:'#1e293b'}}, title),
|
|
React.createElement('div', {style:{display:'flex',alignItems:'flex-end',gap:'8px',height:'200px',borderBottom:'2px solid #e2e8f0',paddingBottom:'8px'}},
|
|
...data.map((d,i) => React.createElement('div', {key:i,style:{flex:1,display:'flex',flexDirection:'column',alignItems:'center',gap:'4px'}},
|
|
React.createElement('span', {style:{fontSize:'11px',fontWeight:'600',color:color||'#6366f1'}}, d[dataKey]),
|
|
React.createElement('div', {style:{width:'100%',background:color||'#6366f1',borderRadius:'4px 4px 0 0',height:((d[dataKey]/max)*160)+'px',minHeight:'4px',transition:'height .3s'}}),
|
|
React.createElement('span', {style:{fontSize:'10px',color:'#64748b'}}, d[xKey]||d.name||d.mois||'')
|
|
))
|
|
)
|
|
);
|
|
}
|
|
// Aliases for Recharts components (render as SimpleChart)
|
|
const LineChart = SimpleChart;
|
|
const BarChart = SimpleChart;
|
|
const Line = () => null;
|
|
const Bar = () => null;
|
|
const XAxis = () => null;
|
|
const YAxis = () => null;
|
|
const CartesianGrid = () => null;
|
|
const Tooltip = () => null;
|
|
const Legend = () => null;
|
|
const ResponsiveContainer = ({children}) => children;
|
|
const PieChart = () => null;
|
|
const Pie = () => null;
|
|
const Cell = () => null;
|
|
const AreaChart = SimpleChart;
|
|
const Area = () => null;
|
|
// Reactstrap aliases
|
|
const Card = ({children,...p}) => React.createElement('div',{style:{background:'#fff',border:'1px solid #e2e8f0',borderRadius:'12px',padding:'20px',margin:'8px',...(p.style||{})}},children);
|
|
const CardBody = ({children}) => React.createElement('div',null,children);
|
|
const Row = ({children}) => React.createElement('div',{style:{display:'flex',flexWrap:'wrap',gap:'16px'}},children);
|
|
const Col = ({children}) => React.createElement('div',{style:{flex:1,minWidth:'200px'}},children);
|
|
const Button = ({children,onClick,...p}) => React.createElement('button',{onClick,style:{padding:'8px 16px',borderRadius:'8px',border:'1px solid #e2e8f0',cursor:'pointer',...(p.style||{})}},children);
|
|
const Container = ({children}) => React.createElement('div',{style:{maxWidth:'1200px',margin:'0 auto'}},children);
|
|
var Header = typeof Header !== 'undefined' ? Header : ({children,title}) => React.createElement('header',{style:{background:'linear-gradient(135deg,#1e293b,#334155)',color:'#fff',padding:'20px 24px',borderRadius:'12px',marginBottom:'20px'}},React.createElement('h1',{style:{margin:0,fontSize:'24px'}},title||children||'Dashboard'));
|
|
var Footer = typeof Footer !== 'undefined' ? Footer : ({children}) => React.createElement('footer',{style:{padding:'16px',textAlign:'center',color:'#94a3b8',fontSize:'12px',marginTop:'24px',borderTop:'1px solid #e2e8f0'}},children||'WEVIA Engine');
|
|
var Sidebar = typeof Sidebar !== 'undefined' ? Sidebar : ({children}) => React.createElement('aside',{style:{width:'250px',background:'#f8fafc',padding:'16px',borderRight:'1px solid #e2e8f0'}},children);
|
|
const NavBar = Header;
|
|
const Navbar = Header;
|
|
var Nav = typeof Nav !== 'undefined' ? Nav : Header;
|
|
const Table = ({children,...p}) => React.createElement('table',{style:{width:'100%',borderCollapse:'collapse',...(p.style||{})}},children);
|
|
const Badge = ({children,color}) => React.createElement('span',{style:{padding:'2px 8px',borderRadius:'12px',fontSize:'12px',background:color||'#e2e8f0'}},children);
|
|
var Header = typeof Header !== 'undefined' ? Header : ({children,...p}) => React.createElement('header',{style:{padding:'16px 24px',background:'#1e293b',color:'#fff',borderRadius:'12px',marginBottom:'20px',...(p.style||{})}},children);
|
|
var Footer = typeof Footer !== 'undefined' ? Footer : ({children}) => React.createElement('footer',{style:{padding:'12px',textAlign:'center',color:'#64748b',marginTop:'20px'}},children);
|
|
var Sidebar = typeof Sidebar !== 'undefined' ? Sidebar : ({children}) => React.createElement('aside',{style:{width:'250px',padding:'16px',background:'#f1f5f9',borderRadius:'12px'}},children);
|
|
var Nav = typeof Nav !== 'undefined' ? Nav : ({children}) => React.createElement('nav',{style:{display:'flex',gap:'12px',marginBottom:'16px'}},children);
|
|
var NavItem = typeof NavItem !== 'undefined' ? NavItem : ({children,active,onClick}) => React.createElement('button',{onClick,style:{padding:'8px 16px',borderRadius:'8px',border:'1px solid #e2e8f0',background:active?'#6366f1':'#fff',color:active?'#fff':'#1e293b',cursor:'pointer'}},children);
|
|
var Input = typeof Input !== 'undefined' ? Input : (p) => React.createElement('input',{...p,style:{padding:'8px 12px',borderRadius:'8px',border:'1px solid #e2e8f0',width:'100%',...(p.style||{})}});
|
|
var Select = typeof Select !== 'undefined' ? Select : ({children,...p}) => React.createElement('select',{...p,style:{padding:'8px 12px',borderRadius:'8px',border:'1px solid #e2e8f0',...(p.style||{})}},children);
|
|
var Alert = typeof Alert !== 'undefined' ? Alert : ({children,type}) => React.createElement('div',{style:{padding:'12px 16px',borderRadius:'8px',background:type==='error'?'#fef2f2':type==='success'?'#f0fdf4':'#eff6ff',border:'1px solid',borderColor:type==='error'?'#fecaca':type==='success'?'#bbf7d0':'#bfdbfe',marginBottom:'12px'}},children);
|
|
var Spinner = typeof Spinner !== 'undefined' ? Spinner : () => React.createElement('div',{style:{width:'24px',height:'24px',border:'3px solid #e2e8f0',borderTop:'3px solid #6366f1',borderRadius:'50%',animation:'spin 1s linear infinite'}});
|
|
var Progress = typeof Progress !== 'undefined' ? Progress : ({value,max}) => React.createElement('div',{style:{height:'8px',background:'#e2e8f0',borderRadius:'4px',overflow:'hidden'}},React.createElement('div',{style:{height:'100%',width:((value||0)/(max||100)*100)+'%',background:'#6366f1',borderRadius:'4px',transition:'width .3s'}}));
|
|
JS;
|
|
$jsxCode = $globals . "\n" . $jsxCode;
|
|
|
|
$hash = substr(md5($jsxCode), 0, 8);
|
|
$filename = "artifact_{$hash}_" . time() . ".html";
|
|
$path = "/var/www/weval/wevia-ia/downloads/{$filename}";
|
|
$url = "/wevia-ia/downloads/{$filename}";
|
|
$safeCode = str_replace('</script>', '<\/script>', $jsxCode);
|
|
|
|
$html = '<!DOCTYPE html><html><head><meta charset="UTF-8">
|
|
<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>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.8/axios.min.js"></script>
|
|
<style>body{font-family:system-ui,sans-serif;padding:20px;margin:0;background:#f8fafc;color:#1e293b}
|
|
h1,h2,h3{color:#1e293b;margin:12px 0}
|
|
table{width:100%;border-collapse:collapse;margin:12px 0}th,td{border:1px solid #e2e8f0;padding:8px 12px;text-align:left}th{background:#f1f5f9}
|
|
ul{margin:8px 0;padding-left:20px}li{margin:4px 0}
|
|
</style></head><body>
|
|
<div id="root"><p style="color:#6366f1;font-style:italic">Chargement du dashboard...</p></div>
|
|
<script>var exports={},module={exports:exports};function require(m){var map={"react":React,"react-dom":ReactDOM,"axios":typeof axios!=="undefined"?axios:{get:function(){return Promise.resolve({data:[]})},post:function(){return Promise.resolve({data:{}})}}}; return map[m]||{}}</script>
|
|
<script type="text/babel">' . $safeCode . '
|
|
try {
|
|
const _names=["Dashboard","DashboardKPI","SupplyChainKPI","DashboardKPISupplyChain","App","Main","__COMP__"];
|
|
let _C=window.__COMP__||null;if(!_C){for(const n of _names){try{_C=eval(n);if(_C)break}catch(e){}}}
|
|
if(_C)ReactDOM.render(React.createElement(_C),document.getElementById("root"));
|
|
else document.getElementById("root").innerHTML="<p>Composant non trouve</p>";
|
|
}catch(e){document.getElementById("root").innerHTML="<pre style=color:red>"+e.message+"</pre>";}
|
|
</script>
|
|
<script>window.onerror=function(m){document.getElementById("root").innerHTML+="<pre style=color:red>"+m+"</pre>"}</script>
|
|
</body></html>';
|
|
|
|
file_put_contents($path, $html);
|
|
$response .= "\n\n---\n\n> \xf0\x9f\x94\x97 **[Voir le dashboard en live]({$url})**\n";
|
|
return ['response' => $response, 'artifact_url' => $url, 'artifact_type' => 'jsx'];
|
|
}
|
|
return null;
|
|
} |