Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica Etichetta: Annullato |
Nessun oggetto della modifica Etichetta: Annullato |
||
| Riga 1: | Riga 1: | ||
< | <html> | ||
<head> | <head> | ||
<meta charset="utf-8"> | |||
<title>Dashboard Operativa – Masticationpedia</title> | |||
<style> | |||
:root{--b:#e5e7eb;--bg:#f9fafb;--card:#ffffff;--txt:#1f2937;--muted:#6b7280;--blue:#0a7cff;--green:#28a745;} | |||
body{background:var(--bg);color:var(--txt);font:16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif} | |||
.toolbar{display:flex;gap:10px;flex-wrap:wrap;margin:16px 0} | |||
.btn{border:1px solid var(--b);background:var(--card);border-radius:10px;padding:10px 14px;font-weight:600;cursor:pointer} | |||
.btn.primary{background:var(--blue);color:#fff;border-color:var(--blue)} | |||
.btn.danger{background:#e11d48;color:#fff;border-color:#e11d48} | |||
.link{font-weight:600} | |||
.dash-box{background:var(--card);border:1px solid var(--b);border-radius:12px;padding:16px;margin-top:14px} | |||
.gpt-card{background:#fff;border:1px solid #e6e6e6;border-radius:10px;padding:16px;box-shadow:0 1px 2px rgba(0,0,0,.04)} | |||
.muted{color:var(--muted)} | |||
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap} | |||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px} | |||
</style> | input[type=text], input[type=number], select, textarea{width:100%;border:1px solid var(--b);border-radius:8px;padding:8px} | ||
textarea{min-height:120px} | |||
ul.plain{list-style:none;padding-left:0;margin:0} | |||
.pill{border:1px solid var(--b);border-radius:999px;padding:2px 8px;font-size:12px;color:var(--muted)} | |||
.list-item{display:flex;align-items:center;gap:8px;justify-content:flex-start;margin:6px 0} | |||
.list-item .actions{display:flex;gap:6px} | |||
code{background:#f3f4f6;border:1px solid #e5e7eb;border-radius:6px;padding:2px 6px} | |||
</style> | |||
</head> | </head> | ||
<body> | <body> | ||
<h2>🔧 Dashboard Operativa – Masticationpedia</h2> | <h2>🔧 Dashboard Operativa – Masticationpedia</h2> | ||
<p class="muted" | <p class="muted">Centro di comando per progetti, API, file e backup</p> | ||
<a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> | <div class="row"> | ||
<button onclick="clearServerLog()">🧹 Svuota Log</button> | <a class="link" href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> | ||
<button class="btn" onclick="clearServerLog()">🧹 Svuota Log</button> | |||
</div> | |||
<!-- | <!-- Pulsanti principali --> | ||
<div | <div class="toolbar"> | ||
<button class=" | <button class="btn" onclick="showBox('api-settings')">⚙️ Connessione API</button> | ||
<button class=" | <button class="btn" onclick="showBox('project-status')">📊 Stato Progetti</button> | ||
<button class=" | <button class="btn" onclick="showBox('test-tools')">🧪 Strumenti di Test</button> | ||
<button class="btn" onclick="showBox('activity-log')">📘 Registro Attività</button> | |||
<button class=" | |||
</div> | </div> | ||
<!-- | <!-- ========================= | ||
<div id="api-settings" class=" | ⚙️ Connessione API (proxy) | ||
========================= --> | |||
<div id="api-settings" class="dash-box" style="display:none"> | |||
<strong>Connessione API (protetta dal server)</strong><br><br> | <strong>Connessione API (protetta dal server)</strong><br><br> | ||
<div class="grid2"> | |||
<div> | |||
<label>Modello</label> | |||
<select id="model-select"> | |||
<option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> | |||
<option value="gpt-4o-mini">gpt-4o-mini</option> | |||
<option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> | |||
</select> | |||
</div> | |||
<div> | |||
<label>Prompt di test</label> | |||
<input id="test-prompt" type="text" value="Dimmi una curiosità sulla mandibola"> | |||
</div> | |||
</div> | </div> | ||
<div style="margin-top:10px | <div class="row" style="margin-top:10px"> | ||
<button | <button class="btn primary" onclick="testAPIConnection()">▶️ Esegui</button> | ||
<span class="pill">via <code>/dashboard/api/openai_project_gpt.php</code></span> | |||
</div> | </div> | ||
<pre id="api-result" class="dash-box" style="white-space:pre-wrap;margin-top:12px"></pre> | |||
</div> | </div> | ||
<!-- === | <!-- ===================== | ||
📊 STATO PROGETTI | |||
===================== --> | |||
<div id="project-status" class="dash-box" style="display:block"> | |||
<strong>📊 Stato Progetti</strong> | |||
</ | |||
<!-- 🧠 Risposta GPT --> | |||
<!-- | <div style="margin-top: 12px"> | ||
<div | <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | ||
<div id="gptResponse" class="gpt-card"> | |||
<em class="muted">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em> | |||
</div> | |||
<label> | |||
< | |||
</ | |||
</div> | </div> | ||
<!-- | <!-- Parametri --> | ||
<div style=" | <div class="dash-box" style="margin-top:12px;border-style:dashed"> | ||
<input id=" | <div class="grid2"> | ||
<input id="p_goal" placeholder="🎯 Obiettivo (es. Attivare SSO MediaWiki classico)"> | |||
<input id="p_audience" placeholder="👥 Pubblico (es. amministratori, sviluppatori)"> | |||
<input id="p_deliverable" placeholder="📦 Output atteso (es. patch LocalSettings + file PHP)"> | |||
<input id="p_constraints" placeholder="⏱️ Vincoli (es. MediaWiki 1.43, evitare conflitti)"> | |||
</div> | |||
<div class="row" style="margin-top:10px"> | |||
<button id="btnAnalyze" class="btn primary">Analizza con GPT</button> | |||
<span class="pill">usa <code>/dashboard/api/openai_project_gpt.php</code></span> | |||
</div> | |||
</div> | </div> | ||
<!-- | <!-- 📂 Gestione Progetti --> | ||
<div class="dash-box" style="margin-top:12px;background:#fff"> | |||
<div | <h4>📂 Gestione Progetti</h4> | ||
<div class="row"> | |||
<input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)" style="max-width:300px"> | |||
<button id="btnCreateProject" class="btn primary">Crea progetto</button> | |||
<div | |||
< | |||
</div> | </div> | ||
<ul id="projectsList" class="plain" style="margin-top:10px"> | |||
<!-- popolato via JS --> | |||
</ul> | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- | <!-- ===================== | ||
<div id=" | 🧪 STRUMENTI DI TEST | ||
< | ===================== --> | ||
<div id="test-tools" class="dash-box" style="display:none"> | |||
< | <strong>Console JS</strong> | ||
<textarea id="codeArea" placeholder="Scrivi codice JS da testare…"></textarea> | |||
<div class="row" style="margin-top:10px"> | |||
<button class="btn" onclick="runTestCode()">▶️ Esegui</button> | |||
<button class="btn" onclick="checkCodeAI()">🔍 Controlla con AI</button> | |||
< | |||
< | |||
</div> | </div> | ||
<pre id="consoleOutput" class="dash-box" style="background:#111;color:#0f0;white-space:pre-wrap"></pre> | |||
</div> | </div> | ||
<!-- | <!-- ===================== | ||
<div | 📘 REGISTRO ATTIVITÀ | ||
===================== --> | |||
<div id="activity-log" class="dash-box" style="display:none"> | |||
<div class="row" style="justify-content:space-between"> | |||
< | <strong>📘 Registro attività</strong> | ||
< | <button class="btn danger" onclick="clearActivityLog()">🧹 Svuota</button> | ||
</div> | </div> | ||
<div id="activityLogContent" class="dash-box" style="margin-top:10px;max-height:260px;overflow:auto;font-family:monospace;font-size:13px"> | |||
<em class="muted">Registro avviato…</em> | |||
<div id=" | |||
<em | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- | <!-- ===================== | ||
<script | JS | ||
===================== --> | |||
<script> | |||
/* ---------- util UI ---------- */ | |||
function showBox(id){ | |||
['api-settings','project-status','test-tools','activity-log'].forEach(x=>{ | |||
const el = document.getElementById(x); | |||
if (el) el.style.display = (x===id?'block':'none'); | |||
}); | |||
} | |||
/* ---------- log ---------- */ | |||
function logActivity(msg){ | |||
const box = document.getElementById('activityLogContent'); | |||
function | if(!box) return; | ||
const | const t = new Date().toLocaleTimeString('it-IT'); | ||
if (! | const line = document.createElement('div'); | ||
line.innerHTML = '<span class="muted">['+t+']</span> ' + msg; | |||
box.prepend(line); | |||
} | |||
function clearActivityLog(){ | |||
const box = document.getElementById('activityLogContent'); | |||
if (box) { box.innerHTML = '<em class="muted">Registro svuotato.</em>'; logActivity('🧹 Log svuotato.'); } | |||
} | } | ||
/* | /* ---------- svuota log server ---------- */ | ||
async function clearServerLog(){ | async function clearServerLog(){ | ||
try{ | try{ | ||
const r = await fetch('/dashboard/api/log_clear.php', { | const r = await fetch('/dashboard/api/log_clear.php', {credentials:'include'}); | ||
const j = await r.json(); | const j = await r.json(); | ||
if (j.ok){ | if (j.ok){ | ||
alert(' | alert('Log svuotato.\n' + (j.cleared||[]).join('\n')); | ||
window.open('/dashboard/api/log_view.php?n=300', '_blank'); | logActivity('🧹 Log server svuotato'); | ||
window.open('/dashboard/api/log_view.php?n=300','_blank'); | |||
}else{ | }else{ | ||
alert(' | alert('Errore svuota log: ' + (j.error||'')); | ||
} | } | ||
}catch(e){ alert(' | }catch(e){ alert('Errore rete: '+e.message); } | ||
} | } | ||
/* | /* ---------- renderer GPT ---------- */ | ||
function renderGpt(text){ | function renderGpt(text){ | ||
const box = document.getElementById('gptResponse'); | const box = document.getElementById('gptResponse'); | ||
| Riga 249: | Riga 188: | ||
.replace(/^\s*#{3}\s?(.*)$/gm,'<h3 style="font-size:18px;margin:14px 0 6px;">$1</h3>') | .replace(/^\s*#{3}\s?(.*)$/gm,'<h3 style="font-size:18px;margin:14px 0 6px;">$1</h3>') | ||
.replace(/^\s*#{2}\s?(.*)$/gm,'<h2 style="font-size:20px;margin:16px 0 8px;">$1</h2>') | .replace(/^\s*#{2}\s?(.*)$/gm,'<h2 style="font-size:20px;margin:16px 0 8px;">$1</h2>') | ||
.replace(/^\s*#\s?(.*)$/gm, | .replace(/^\s*#\s?(.*)$/gm,'<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>') | ||
.replace(/^\s*-\s+(.*)$/gm, | .replace(/^\s*-\s+(.*)$/gm,'<li>$1</li>'); | ||
html = html.replace(/(?:<li>.*<\/li>\n?)+/gs, m => `<ul style="margin:8px 0 14px 22px;">${m}</ul>`); | html = html.replace(/(?:<li>.*<\/li>\n?)+/gs, m => `<ul style="margin:8px 0 14px 22px;">${m}</ul>`); | ||
html = html.split('\n').map(line=>{ | html = html.split('\n').map(line=>{ | ||
if (/^<h\d|^<ul|^<li|^<\/ul>/.test(line)) return line; | if (/^<h\d|^<ul|^<li|^<\/ul>/.test(line)) return line; | ||
if (line.trim()==='') return ''; | if (line.trim()==='') return ''; | ||
return `<p style="margin: | return `<p style="margin:8px 0;">${line}</p>`; | ||
}).join(''); | }).join(''); | ||
box.innerHTML = html; | box.innerHTML = html; | ||
} | } | ||
/* | /* ---------- prompt per GPT ---------- */ | ||
function buildPromptForGPT(){ | function buildPromptForGPT(){ | ||
const c = | const g = (document.getElementById('p_goal')?.value || '').trim(); | ||
const a = (document.getElementById('p_audience')?.value || '').trim(); | |||
const d = (document.getElementById('p_deliverable')?.value || '').trim(); | |||
const c = (document.getElementById('p_constraints')?.value || '').trim(); | |||
const out = []; | const out = []; | ||
out.push('Rispondi in | out.push('Sei un project designer senior. Rispondi in italiano con piano chiaro, operativo.'); | ||
out.push('\n# | if (g) out.push('\\n# Obiettivo\\n'+g); | ||
if ( | if (a) out.push('\\n# Pubblico\\n'+a); | ||
if ( | if (d) out.push('\\n# Output atteso\\n'+d); | ||
if (c | if (c) out.push('\\n# Vincoli\\n'+c); | ||
out.push('\\n# Output richiesto\\n- Executive Summary\\n- Punti di forza & rischi\\n- Piano step-by-step (milestones)\\n- Risorse & budget\\n- Metriche di successo'); | |||
return out.join('\n'); | return out.join('\\n'); | ||
} | } | ||
/* | /* ---------- analisi con GPT (via proxy server) ---------- */ | ||
async function runProjectAnalysis(){ | async function runProjectAnalysis(){ | ||
const btn = document.getElementById('btnAnalyze'); | const btn = document.getElementById('btnAnalyze'); | ||
btn.disabled = true; btn.textContent = 'Analizzo…'; | |||
btn.disabled = true; btn.textContent='Analizzo…'; | |||
try{ | try{ | ||
const model = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13'; | |||
const prompt = buildPromptForGPT(); | const prompt = buildPromptForGPT(); | ||
const | logActivity('🤖 Analisi GPT via proxy – modello: <b>'+model+'</b>'); | ||
method:'POST', headers:{'Content-Type':'application/json'}, credentials:'include', | |||
body: JSON.stringify({ prompt, | const r = await fetch('/dashboard/api/openai_project_gpt.php', { | ||
method:'POST', | |||
headers:{'Content-Type':'application/json'}, | |||
credentials:'include', | |||
body: JSON.stringify({ model, prompt }) | |||
}); | |||
const j = await r.json(); | |||
if (j.status==='ok' && j.result){ | |||
renderGpt(j.result); | |||
logActivity('✅ Analisi GPT completata'); | |||
}else{ | |||
renderGpt('❌ Errore: ' + (j.error||'risposta vuota')); | |||
logActivity('❌ Errore GPT: ' + (j.error||'vuota')); | |||
} | |||
}catch(e){ | |||
renderGpt('❌ Errore rete: '+e.message); | |||
}finally{ | |||
btn.disabled=false; btn.textContent='Analizza con GPT'; | |||
} | |||
} | |||
/* ---------- API progetto ---------- */ | |||
const API = { | |||
list: '/dashboard/api/project_list.php', | |||
create: '/dashboard/api/project_create.php', | |||
del: '/dashboard/api/project_delete.php', | |||
backup: '/dashboard/api/project_backup.php' | |||
}; | |||
let currentProject = null; | |||
async function loadProjects(){ | |||
const ul = document.getElementById('projectsList'); | |||
if (!ul) return; | |||
ul.innerHTML = '<li class="muted">Carico…</li>'; | |||
try{ | |||
const r = await fetch(API.list, {credentials:'include', cache:'no-store'}); | |||
const t = await r.text(); let j; | |||
try{ j = JSON.parse(t); }catch(_){ throw new Error('Risposta non-JSON'); } | |||
if(!j.ok) throw new Error(j.error||'Lista progetti fallita'); | |||
ul.innerHTML = ''; | |||
(j.projects||[]).forEach(p=>{ | |||
const li = document.createElement('li'); | |||
li.className = 'list-item'; | |||
const name = document.createElement('strong'); | |||
name.textContent = p.name; | |||
name.style.fontWeight = (p.name===currentProject?'800':'600'); | |||
const actions = document.createElement('div'); actions.className='actions'; | |||
const bOpen = document.createElement('button'); bOpen.className='btn'; bOpen.textContent='Apri'; | |||
bOpen.onclick = ()=>{ currentProject = p.name; loadProjects(); logActivity('📁 Progetto selezionato: <b>'+p.name+'</b>'); }; | |||
const bZip = document.createElement('button'); bZip.className='btn'; bZip.textContent='ZIP'; | |||
bZip.title='Crea backup zip'; bZip.onclick = ()=>backupProject(p.name); | |||
const bDel = document.createElement('button'); bDel.className='btn danger'; bDel.textContent='🗑️'; | |||
bDel.title='Sposta nel cestino'; bDel.onclick = ()=>deleteProject(p.name); | |||
actions.append(bOpen,bZip,bDel); | |||
li.append(name, actions); | |||
ul.appendChild(li); | |||
}); | }); | ||
if (j. | if(!j.projects || !j.projects.length){ | ||
ul.innerHTML = '<li class="muted">Nessun progetto presente.</li>'; | |||
}catch(e){ | } | ||
}catch(e){ | |||
ul.innerHTML = '<li class="muted">Errore: '+e.message+'</li>'; | |||
} | |||
} | } | ||
async function createProject(){ | async function createProject(){ | ||
const input = document.getElementById('newProjectName'); | const input = document.getElementById('newProjectName'); | ||
| Riga 304: | Riga 302: | ||
if (!name) return alert('Inserisci un nome progetto (A-Z 0-9 _ -)'); | if (!name) return alert('Inserisci un nome progetto (A-Z 0-9 _ -)'); | ||
try{ | try{ | ||
const body = new URLSearchParams({ name }); | const body = new URLSearchParams({name}); | ||
const r = await fetch( | const r = await fetch(API.create,{method:'POST',body,credentials:'include'}); | ||
const j = await r.json(); | const j = await r.json(); | ||
if (!j.ok) throw new Error(j.error||'create failed'); | if(!j.ok) throw new Error(j.error||'create failed'); | ||
input.value=''; | input.value=''; currentProject = name; | ||
logActivity('📁 Creato progetto: <b>'+name+'</b>'); | |||
loadProjects(); | |||
}catch(e){ alert(' | }catch(e){ alert('Errore creazione: '+e.message); } | ||
} | } | ||
async function deleteProject(name){ | |||
async function | if(!confirm('Spostare "'+name+'" nel cestino?')) return; | ||
try{ | try{ | ||
const r = await fetch( | const body = new URLSearchParams({name}); | ||
const r = await fetch(API.del,{method:'POST',body,credentials:'include'}); | |||
const j = await r.json(); | const j = await r.json(); | ||
if (!j.ok) throw new Error(j.error||' | if(!j.ok) throw new Error(j.error||'delete failed'); | ||
if(currentProject===name) currentProject=null; | |||
if ( | logActivity('🗑️ Progetto nel cestino: <b>'+name+'</b>'); | ||
loadProjects(); | |||
}catch(e){ alert('Errore eliminazione: '+e.message); } | |||
}catch(e){ | |||
} | } | ||
async function backupProject(name){ | |||
async function | try{ | ||
const | const body = new URLSearchParams({name}); | ||
const r = await fetch(API.backup,{method:'POST',body,credentials:'include'}); | |||
const j = await r.json(); | |||
if(!j.ok) throw new Error(j.error||'backup failed'); | |||
alert('Backup creato: '+j.zip); | |||
logActivity('🗜️ ZIP creato per <b>'+name+'</b>'); | |||
}catch(e){ alert('Errore backup: '+e.message); } | |||
} | |||
/* ---------- test tools ---------- */ | |||
if (! | function runTestCode(){ | ||
const ta = document.getElementById('codeArea'); | |||
if(!ta) return; | |||
try{ new Function(ta.value||'')(); } | |||
catch(e){ alert('Errore nel codice: '+e.message); } | |||
} | |||
const | /* ---------- checkCodeAI (usa ai_check.php) ---------- */ | ||
async function checkCodeAI(){ | |||
const codeEl = document.getElementById('codeArea'); | |||
const outEl = document.getElementById('consoleOutput'); | |||
if(!codeEl || !outEl){ alert('Manca #codeArea o #consoleOutput'); return; } | |||
const code = codeEl.value||''; | |||
if(!code.trim()) return alert('Incolla del codice da analizzare.'); | |||
outEl.textContent = '⏳ Invio al correttore AI…'; | |||
try{ | try{ | ||
const r = await fetch('/dashboard/api/ | const r = await fetch('/dashboard/api/ai_check.php', { | ||
method:'POST | method:'POST', | ||
headers:{'Content-Type':'application/ | headers:{'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8'}, | ||
body: | body:new URLSearchParams({language:'php', code}) | ||
}); | }); | ||
const | const j = await r.json(); | ||
if(j.ok){ | |||
let txt=''; if(j.lint && j.lint.trim()) txt+='=== LINT ===\\n'+j.lint+'\\n\\n'; | |||
txt+=(j.analysis||'Nessun output.'); | |||
} else { | outEl.textContent=txt; | ||
}else{ | |||
outEl.textContent='❌ Errore: '+(j.error||'sconosciuto'); | |||
if(j.raw) outEl.textContent+='\\n\\nRAW:\\n'+j.raw; | |||
} | } | ||
}catch(e){ | }catch(e){ outEl.textContent='❌ Errore rete: '+e.message; } | ||
} | } | ||
/* | /* ---------- connessione API test ---------- */ | ||
async function | async function testAPIConnection(){ | ||
const | const prompt = (document.getElementById('test-prompt')?.value||'').trim(); | ||
const | const model = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13'; | ||
const | const out = document.getElementById('api-result'); | ||
if (! | if(!prompt){ out.textContent='⚠️ Inserisci un prompt di test.'; return; } | ||
out.textContent='⏳ Contatto il proxy sul server…'; | |||
try{ | try{ | ||
const | const res = await fetch('/dashboard/api/openai_project_gpt.php', { | ||
method:'POST', | |||
const | headers:{'Content-Type':'application/json'}, | ||
if ( | credentials:'include', | ||
body: JSON.stringify({ prompt, model }) | |||
} else { | }); | ||
const data = await res.json(); | |||
if (data.status==='ok' && data.result){ | |||
out.textContent = '✅ Risposta: ' + data.result; | |||
logActivity('✅ Proxy OK – modello '+model); | |||
}else{ | |||
out.textContent = '❌ Errore: ' + (data.error||'risposta vuota'); | |||
} | } | ||
}catch(e){ | }catch(e){ out.textContent='🚫 Errore di rete: '+e.message; } | ||
} | } | ||
/* | /* ---------- init ---------- */ | ||
document.addEventListener('DOMContentLoaded', ()=>{ | document.addEventListener('DOMContentLoaded', ()=>{ | ||
// mostra subito Stato Progetti | |||
showBox('project-status'); | |||
// bind | |||
document.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis); | document.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis); | ||
document.getElementById('btnCreateProject')?.addEventListener('click', createProject); | document.getElementById('btnCreateProject')?.addEventListener('click', createProject); | ||
// carica lista progetti | |||
loadProjects(); | loadProjects(); | ||
}); | }); | ||
</script> | </script> | ||
</body> | </body> | ||
</html> | </html> | ||
Versione delle 10:57, 21 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup
📊 Stato Progetti
Qui apparirà la risposta generata da GPT sull’analisi del progetto…
usa
/dashboard/api/openai_project_gpt.php