Files
wevads-platform/scripts/sidebar-admin.php
2026-02-26 04:53:11 +01:00

423 lines
16 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$excludeDirs = ['plugins','css','js','images','uploads','chrome','tmp','storage','assets','styles','videos','media','data','config','memes','artifacts','scripts','fonts','webfonts'];
$excludeFiles = ['index.php','.htaccess','click.php','lead.php','open.php','unsub.php'];
function scanAll($dir, $base = '', $exDirs = [], $exFiles = []) {
$files = [];
if (!is_dir($dir)) return $files;
foreach (scandir($dir) as $f) {
if ($f[0] === '.') continue;
$path = $dir . '/' . $f;
$rel = $base ? $base . '/' . $f : $f;
if (is_dir($path)) {
if (!in_array($f, $exDirs)) $files = array_merge($files, scanAll($path, $rel, $exDirs, $exFiles));
} else {
$ext = pathinfo($f, PATHINFO_EXTENSION);
if (in_array($ext, ['php','html']) && !in_array($f, $exFiles)) $files[] = ['name' => $f, 'path' => '/' . $rel];
}
}
return $files;
}
$allFiles = scanAll('/opt/wevads/public', '', $excludeDirs, $excludeFiles);
usort($allFiles, fn($a,$b) => strcmp($a['name'], $b['name']));
function parseMenu($file) {
$html = file_get_contents($file);
$html = preg_replace('/<!--.*?-->/s', '', $html);
$menu = [];
$seen = [];
// Liens directs en premier (dans l'ordre du vrai menu)
$directLinks = [
['t' => 'Dashboard', 'u' => '/dashboard.html', 'd' => true],
['t' => '🎛️ Control Hub', 'u' => '/control-hub.php', 'd' => true],
['t' => '🏆 Winning Config', 'u' => '/deliverads/brain-dashboard.php', 'd' => true],
];
foreach ($directLinks as $dl) {
$menu[] = $dl;
$seen[$dl['t']] = true;
}
// Nav-toggle menus
$parts = preg_split('/(<a[^>]*nav-toggle[^>]*>)/', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; $i < count($parts); $i += 2) {
if (!isset($parts[$i+1])) continue;
$section = $parts[$i] . $parts[$i+1];
if (preg_match('/<span class="title"[^>]*>([^<]+)/', $section, $pm)) {
$parentTitle = trim($pm[1]);
if (isset($seen[$parentTitle])) continue;
$seen[$parentTitle] = true;
$children = [];
if (preg_match('/<ul class="sub-menu">(.*?)(<\/ul>|<li class="nav-item)/s', $section, $sm)) {
preg_match_all('/href="([^"]+)"[^>]*>.*?<span class="title"[^>]*>([^<]+)/s', $sm[1], $cm, PREG_SET_ORDER);
foreach ($cm as $c) {
$url = preg_replace('/\{echo \$app\[.base_url.\]\}/', '', trim($c[1]));
$children[] = ['t' => trim($c[2]), 'u' => $url ?: '#'];
$seen[trim($c[2])] = true;
}
}
$menu[] = ['t' => $parentTitle, 'c' => $children];
}
}
// Ajouter Smart Report après Send APIs (position ~index 10)
$smartReport = ['t' => 'Smart Report', 'u' => '/dashboard/top-stats.html', 'd' => true];
$revenueReport = ['t' => 'Revenu Report', 'u' => '/statistics/full-report.html', 'd' => true];
// Trouver position après Send APIs
$insertPos = 0;
foreach ($menu as $i => $m) {
if (strpos($m['t'], 'Send APIs') !== false) {
$insertPos = $i + 1;
break;
}
}
if ($insertPos > 0) {
array_splice($menu, $insertPos, 0, [$smartReport]);
// Revenu Report après Production
foreach ($menu as $i => $m) {
if (strpos($m['t'], 'Tracking') !== false) {
array_splice($menu, $i, 0, [$revenueReport]);
break;
}
}
}
return $menu;
}
$menuStructure = parseMenu('/opt/wevads/app/views/includes/menu.html');
$menuJson = json_encode($menuStructure, JSON_UNESCAPED_UNICODE);
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Sidebar Admin</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui;background:#030308;color:#fff;min-height:100vh}
.hdr{display:flex;justify-content:space-between;align-items:center;padding:10px 15px;background:linear-gradient(135deg,rgba(0,255,255,0.1),rgba(191,0,255,0.1));border-bottom:1px solid rgba(0,255,255,0.3)}
.hdr h1{font-size:1em;color:#0ff}
.stats span{margin:0 8px;color:#0f8}
.btn{padding:5px 10px;border:none;border-radius:4px;cursor:pointer;font-size:0.75em;background:#0f8;color:#000;font-weight:bold}
.btn-s{background:transparent;border:1px solid rgba(0,255,255,0.3);color:#fff}
.btn-r{background:#f36;color:#fff}
.main{display:grid;grid-template-columns:1fr 40px 1fr;height:calc(100vh - 45px)}
.pan{background:#0a0a15;display:flex;flex-direction:column}
.pan-h{padding:8px;border-bottom:1px solid rgba(0,255,255,0.2);font-size:0.8em;color:#0ff}
.pan-b{flex:1;overflow-y:auto;padding:5px}
.srch{width:100%;padding:6px;background:#000;border:1px solid rgba(0,255,255,0.2);border-radius:4px;color:#fff;margin-bottom:5px;font-size:0.75em}
.ctr{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;background:#050508;border-left:1px solid rgba(0,255,255,0.2);border-right:1px solid rgba(0,255,255,0.2)}
.tb{width:28px;height:28px;border:2px solid #0ff;border-radius:50%;background:rgba(0,255,255,0.1);color:#0ff;cursor:pointer}
.tb:hover{background:#0ff;color:#000}
.it{display:flex;align-items:center;padding:4px 6px;margin:2px 0;background:rgba(0,0,0,0.3);border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:0.7em}
.it:hover{border-color:rgba(0,255,255,0.3)}
.it.sel{background:rgba(0,255,255,0.15);border-color:#0ff}
.it.del{background:rgba(255,100,100,0.1);border-color:rgba(255,100,100,0.3)}
.nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pt{font-size:0.65em;color:#667;margin-left:5px;max-width:120px;overflow:hidden;text-overflow:ellipsis}
.par{background:rgba(0,255,255,0.08);border:1px solid rgba(0,255,255,0.2);font-weight:bold;margin-top:5px}
.dir{background:rgba(0,255,136,0.08);border:1px solid rgba(0,255,136,0.2)}
.ch{margin-left:10px;padding-left:5px;border-left:2px solid rgba(0,255,255,0.2)}
.chi{font-size:0.65em;padding:3px 5px}
.mb{width:16px;height:16px;border:none;border-radius:2px;background:rgba(0,255,255,0.1);color:#0ff;cursor:pointer;margin-left:2px;font-size:0.55em}
.mb:hover{background:#0ff;color:#000}
.mb.x{background:rgba(255,50,100,0.1);color:#f36}
.mb.x:hover{background:#f36;color:#fff}
.cb{background:none;border:none;color:#0ff;cursor:pointer;width:12px;font-size:0.6em}
.deld{margin-top:8px;padding-top:8px;border-top:1px dashed rgba(255,100,100,0.3)}
.deld-h{font-size:0.65em;color:#f66;margin-bottom:4px}
::-webkit-scrollbar{width:4px}
::-webkit-scrollbar-thumb{background:rgba(0,255,255,0.3);border-radius:2px}
</style>
</head>
<body>
<div class="hdr">
<h1>🎛️ Sidebar Admin</h1>
<div class="stats"><span><?=count($allFiles)?> fichiers</span><span id="mc2"><?=count($menuStructure)?> menus</span></div>
<div>
<button class="btn btn-s" onclick="location.reload()">↻</button>
<button class="btn btn-s" onclick="eAll()">▼</button>
<button class="btn btn-s" onclick="cAll()">▲</button>
<button class="btn" onclick="saveToFile()">💾 Appliquer</button>
</div>
</div>
<div class="main">
<div class="pan">
<div class="pan-h">📁 Fichiers (<?=count($allFiles)?>)</div>
<div class="pan-b">
<input class="srch" id="sF" placeholder="🔍 Rechercher..." oninput="fF()">
<div id="fL"><?php foreach($allFiles as $i=>$f):?><div class="it" data-i="<?=$i?>" data-n="<?=strtolower($f['name'])?>" data-p="<?=$f['path']?>" onclick="sF(this)"><span class="nm"><?=$f['name']?></span><span class="pt"><?=$f['path']?></span></div><?php endforeach;?></div>
<div id="dS" class="deld" style="display:none"><div class="deld-h">🗑️ Supprimés (clic=restaurer)</div><div id="dL"></div></div>
</div>
</div>
<div class="ctr"><button class="tb" onclick="add()">→</button><button class="tb" onclick="rem()">←</button></div>
<div class="pan">
<div class="pan-h">📋 Menu (<span id="mc"><?=count($menuStructure)?></span>) <button class="mb" onclick="addP()" style="float:right">+</button></div>
<div class="pan-b">
<input class="srch" id="sM" placeholder="🔍 Rechercher..." oninput="fM()">
<div id="mL"></div>
</div>
</div>
</div>
<script>
let M=<?=$menuJson?>;
let D=[];
let selIdx=null; // {type:'file'|'parent'|'child', i:number, j?:number}
let col={};
function r(){
let h='';
M.forEach((p,i)=>{
const isDirect=p.d;
const isCollapsed=col[i];
const childCount=p.c?p.c.length:0;
h+=`<div class="it ${isDirect?'dir':'par'}" data-type="parent" data-i="${i}" onclick="selP(${i})">`;
if(!isDirect && childCount>0){
h+=`<button class="cb" onclick="event.stopPropagation();tg(${i})">${isCollapsed?'+':'-'}</button>`;
}else{
h+=`<span style="width:12px;display:inline-block"></span>`;
}
h+=`<span class="nm">${isDirect?'🔗':'📁'} ${p.t}</span>`;
h+=`<span class="pt">${isDirect?p.u:childCount+' items'}</span>`;
if(!isDirect) h+=`<button class="mb" onclick="event.stopPropagation();addToParent(${i})">+</button>`;
h+=`<button class="mb" onclick="event.stopPropagation();editP(${i})">✏</button>`;
h+=`<button class="mb x" onclick="event.stopPropagation();delP(${i})">×</button>`;
h+=`</div>`;
if(!isDirect && childCount>0 && !isCollapsed){
h+=`<div class="ch">`;
p.c.forEach((c,j)=>{
h+=`<div class="it chi" data-type="child" data-i="${i}" data-j="${j}" onclick="selC(${i},${j})">`;
h+=`<span class="nm">${c.t}</span>`;
h+=`<span class="pt">${c.u}</span>`;
h+=`<button class="mb" onclick="event.stopPropagation();editC(${i},${j})">✏</button>`;
h+=`<button class="mb x" onclick="event.stopPropagation();delC(${i},${j})">×</button>`;
h+=`</div>`;
});
h+=`</div>`;
}
});
document.getElementById('mL').innerHTML=h;
document.getElementById('mc').textContent=M.length;
document.getElementById('mc2').textContent=M.length+' menus';
rD();
updateSel();
}
function rD(){
const ds=document.getElementById('dS');
const dl=document.getElementById('dL');
if(D.length===0){ds.style.display='none';return;}
ds.style.display='block';
let h='';
D.forEach((d,i)=>{
h+=`<div class="it del" onclick="restore(${i})"><span class="nm">🔄 ${d.t}</span><span class="pt">${d.u||''}</span></div>`;
});
dl.innerHTML=h;
}
function restore(i){
const item=D[i];
D.splice(i,1);
M.push(item);
r();
}
function updateSel(){
document.querySelectorAll('.it').forEach(e=>e.classList.remove('sel'));
if(selIdx){
if(selIdx.type==='file'){
document.querySelector(`#fL .it[data-i="${selIdx.i}"]`)?.classList.add('sel');
}else if(selIdx.type==='parent'){
document.querySelector(`#mL .it[data-type="parent"][data-i="${selIdx.i}"]`)?.classList.add('sel');
}else if(selIdx.type==='child'){
document.querySelector(`#mL .it[data-type="child"][data-i="${selIdx.i}"][data-j="${selIdx.j}"]`)?.classList.add('sel');
}
}
}
function tg(i){col[i]=!col[i];r();}
function eAll(){col={};r();}
function cAll(){M.forEach((p,i)=>{if(p.c&&p.c.length)col[i]=true;});r();}
function fF(){
const q=document.getElementById('sF').value.toLowerCase();
document.querySelectorAll('#fL .it').forEach(e=>{
const n=e.dataset.n||'';
const p=e.dataset.p||'';
e.style.display=(n.includes(q)||p.toLowerCase().includes(q))?'flex':'none';
});
}
function fM(){
const q=document.getElementById('sM').value.toLowerCase();
document.querySelectorAll('#mL .it').forEach(e=>{
const nm=e.querySelector('.nm');
if(nm){
const t=nm.textContent.toLowerCase();
e.style.display=t.includes(q)?'flex':'none';
}
});
document.querySelectorAll('#mL .ch').forEach(e=>{
const visible=Array.from(e.querySelectorAll('.it')).some(c=>c.style.display!=='none');
e.style.display=visible?'block':'none';
});
}
function sF(el){
selIdx={type:'file',i:parseInt(el.dataset.i),name:el.querySelector('.nm').textContent,path:el.dataset.p};
updateSel();
}
function selP(i){
selIdx={type:'parent',i:i};
updateSel();
}
function selC(i,j){
selIdx={type:'child',i:i,j:j};
updateSel();
}
function add(){
if(!selIdx||selIdx.type!=='file'){alert('Sélectionnez un fichier à gauche!');return;}
// Trouver le parent sélectionné
const selParent=document.querySelector('#mL .it.sel[data-type="parent"]');
if(!selParent){alert('Sélectionnez un menu 📁 à droite!');return;}
const pi=parseInt(selParent.dataset.i);
if(M[pi].d){alert('Impossible d\'ajouter à un lien direct!');return;}
if(!M[pi].c)M[pi].c=[];
M[pi].c.push({t:selIdx.name,u:selIdx.path});
selIdx=null;
r();
}
function addToParent(i){
if(!selIdx||selIdx.type!=='file'){alert('Sélectionnez un fichier à gauche!');return;}
if(!M[i].c)M[i].c=[];
M[i].c.push({t:selIdx.name,u:selIdx.path});
selIdx=null;
r();
}
function rem(){
if(!selIdx){alert('Sélectionnez un élément!');return;}
if(selIdx.type==='parent'){
D.push(M[selIdx.i]);
M.splice(selIdx.i,1);
}else if(selIdx.type==='child'){
D.push(M[selIdx.i].c[selIdx.j]);
M[selIdx.i].c.splice(selIdx.j,1);
}
selIdx=null;
r();
}
function addP(){
const t=prompt('Nom du nouveau menu:');
if(t&&t.trim()){
M.push({t:t.trim(),c:[]});
r();
}
}
function editP(i){
const p=M[i];
const t=prompt('Titre:',p.t);
if(t&&t.trim()){
p.t=t.trim();
if(p.d){
const u=prompt('URL:',p.u);
if(u)p.u=u;
}
r();
}
}
function delP(i){
D.push(M[i]);
M.splice(i,1);
if(selIdx&&selIdx.type==='parent'&&selIdx.i===i)selIdx=null;
r();
}
function editC(i,j){
const c=M[i].c[j];
const t=prompt('Titre:',c.t);
if(t&&t.trim()){
c.t=t.trim();
const u=prompt('URL:',c.u);
if(u)c.u=u;
r();
}
}
function delC(i,j){
D.push(M[i].c[j]);
M[i].c.splice(j,1);
if(selIdx&&selIdx.type==='child'&&selIdx.i===i&&selIdx.j===j)selIdx=null;
r();
}
function saveToFile(){
let html=`<div class="page-sidebar navbar-collapse collapse">
<ul class="page-sidebar-menu page-header-fixed page-sidebar-menu-closed" data-keep-expanded="false" data-auto-scroll="false" data-slide-speed="200" style="padding-top: 20px">
<li class="sidebar-search-box"><input type="text" id="menu-search" class="form-control" placeholder="🔍 Rechercher..." onkeyup="filterMenu(this.value)"></li>\n`;
M.forEach(p=>{
if(p.d){
html+=`<li class="nav-item"><a href="${p.u}" class="nav-link"><i class="fa fa-link"></i><span class="title">${p.t}</span></a></li>\n`;
}else{
const icon=p.t.includes('IA')?'fa-brain':p.t.includes('Cloud')?'fa-cloud':p.t.includes('Domain')?'fa-globe':p.t.includes('Send')?'fa-envelope':p.t.includes('N8N')?'fa-project-diagram':'fa-folder';
html+=`<li class="nav-item"><a href="javascript:;" class="nav-link nav-toggle"><i class="fa ${icon}"></i><span class="title">${p.t}</span><span class="arrow"></span></a>\n<ul class="sub-menu">\n`;
if(p.c){
p.c.forEach(c=>{
html+=`<li><a href="${c.u}" class="nav-link"><span class="title">${c.t}</span></a></li>\n`;
});
}
html+=`</ul></li>\n`;
}
});
html+=`</ul></div>`;
// Sauvegarder via API
fetch('/api/save-menu.php',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({html:html})
}).then(r=>r.json()).then(d=>{
if(d.success){
alert('✅ Menu sauvegardé!\nRechargez la page principale pour voir les changements.');
}else{
// Fallback: copier dans clipboard
navigator.clipboard.writeText(html).then(()=>{
alert('⚠️ API indisponible.\n\nHTML copié dans le presse-papier.\nCollez dans:\n/opt/wevads/app/views/includes/menu.html');
});
}
}).catch(()=>{
navigator.clipboard.writeText(html).then(()=>{
alert('HTML copié!\nCollez dans menu.html puis:\nsystemctl restart apache2');
});
});
}
r();
</script>
</body>
</html>