Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica Etichetta: Ripristino manuale |
Nessun oggetto della modifica |
||
| Riga 6: | Riga 6: | ||
<button onclick="clearServerLog()">🧹 Svuota Log</button> | <button onclick="clearServerLog()">🧹 Svuota Log</button> | ||
<div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> | <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> | ||
<button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button> | <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button> | ||
<button class="dashboard-toggle" | |||
onclick="(window.showMpAI ? showMpAI() : (function(){var el=document.getElementById('mpAI'); if(el){ el.style.display='block'; el.scrollIntoView({behavior:'smooth'});} else { alert('#mpAI non trovato'); } })())"> | |||
🤖 Masticationpedia AI | |||
</button> | |||
</div> | </div> | ||
<!-- ============== MASTICATIONPEDIA AI | <!-- ============== MASTICATIONPEDIA AI ============== --> | ||
<div id="mpAI" class="mpai-root" style="display:none"> | <div id="mpAI" class="mpai-root" style="display:none"> | ||
<div class="mpai-header"> | <div class="mpai-header"> | ||
<div class="mpai-title">🤖 Masticationpedia <b>AI</b></div> | <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div> | ||
| Riga 44: | Riga 27: | ||
<div class="mpai-grid"> | <div class="mpai-grid"> | ||
<aside class="mpai-col mpai-left"> | <aside class="mpai-col mpai-left"> | ||
<h4>📂 Progetti</h4> | <h4>📂 Progetti</h4> | ||
| Riga 59: | Riga 41: | ||
</aside> | </aside> | ||
<main class="mpai-col mpai-center"> | <main class="mpai-col mpai-center"> | ||
<div id="mpai-uploads" class="mpai-uploads" data-state="idle"> | <div id="mpai-uploads" class="mpai-uploads" data-state="idle"> | ||
<div class="mpai-dropzone" id="mpai-dropzone"> | <div class="mpai-dropzone" id="mpai-dropzone"> | ||
| Riga 75: | Riga 55: | ||
</div> | </div> | ||
<div id="mpai-chat" class="mpai-chat"> | <div id="mpai-chat" class="mpai-chat"> | ||
<div class="mpai-msg mpai-hint"> | <div class="mpai-msg mpai-hint"> | ||
| Riga 82: | Riga 61: | ||
</div> | </div> | ||
<div class="mpai-composer"> | <div class="mpai-composer"> | ||
<textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> | <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> | ||
| Riga 89: | Riga 67: | ||
</main> | </main> | ||
<aside class="mpai-col mpai-right"> | <aside class="mpai-col mpai-right"> | ||
<h4>⚙️ Impostazioni</h4> | <h4>⚙️ Impostazioni</h4> | ||
| Riga 114: | Riga 91: | ||
<!-- ============ STILI (CSS) ============ --> | <!-- ============ STILI (CSS) ============ --> | ||
<style> | <style> | ||
.mpai-root{--bg:#f7f8fa;--card:#fff;--muted:#6b7280;--line:#e5e7eb;--text:#111827; | .mpai-root{--bg:#f7f8fa;--card:#fff;--muted:#6b7280;--line:#e5e7eb;--text:#111827;--primary:#0a7cff;--danger:#e74c3c;--accent:#eef2ff;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:var(--text)} | ||
.mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px} | .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px} | ||
.mpai-title{font-size:20px;font-weight:700} | .mpai-title{font-size:20px;font-weight:700} | ||
| Riga 152: | Riga 127: | ||
.mpai-composer{display:flex;gap:8px;align-items:flex-end} | .mpai-composer{display:flex;gap:8px;align-items:flex-end} | ||
.mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px} | .mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px} | ||
@media (max-width: 1100px){ | @media (max-width:1100px){.mpai-grid{grid-template-columns:1fr}.mpai-left,.mpai-right{max-height:none}} | ||
</style> | </style> | ||
<!-- ============ JS (tutto qui) ============ --> | <!-- ============ JS (tutto qui) ============ --> | ||
<script> | <script> | ||
// --- | // --- utility legacy --- | ||
function toggleDashboardBox(id){ | function toggleDashboardBox(id){ | ||
var el = document.getElementById(id); | var el=document.getElementById(id); if(!el) return; | ||
var ai=document.getElementById('mpAI'); if(ai) ai.style.display='none'; | |||
var ai = document.getElementById('mpAI'); if (ai) ai.style.display='none'; | el.style.display=(el.style.display==='none'||!el.style.display)?'block':'none'; | ||
el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none'; | |||
} | } | ||
async function clearServerLog(){ | async function clearServerLog(){ | ||
try{ | try{ | ||
const r = await fetch('/dashboard/api/log_clear.php',{method:'POST',credentials:'include'}); | const r=await fetch('/dashboard/api/log_clear.php',{method:'POST',credentials:'include'}); | ||
const j = await r.json().catch(()=>({ok:false})); | const j=await r.json().catch(()=>({ok:false})); | ||
alert(j && j.ok ? 'Log svuotato.' : 'Impossibile svuotare il log.'); | alert(j && j.ok ? 'Log svuotato.' : 'Impossibile svuotare il log.'); | ||
}catch(e){ alert('Errore rete: '+e.message); } | }catch(e){ alert('Errore rete: '+e.message); } | ||
} | } | ||
function showMpAI(){ | function showMpAI(){ | ||
['api-settings','project-status','chatgpt-plus','test-tools','activity-log'] | ['api-settings','project-status','chatgpt-plus','test-tools','activity-log'].forEach(id=>{ | ||
var el=document.getElementById(id); if(el) el.style.display='none'; | |||
var ai = document.getElementById('mpAI'); | }); | ||
var ai=document.getElementById('mpAI'); if(ai) ai.style.display='block'; | |||
} | } | ||
| Riga 185: | Riga 155: | ||
(function(){ | (function(){ | ||
// Stato | // Stato | ||
let currentProject = null | let currentProject=null, sessionId=null, sessionMeta=null; | ||
let history=[], attachments=[]; | |||
let history = [] | |||
// Shortcuts | // Shortcuts | ||
| Riga 196: | Riga 163: | ||
const fileListEl = $('#mpai-file-list'); | const fileListEl = $('#mpai-file-list'); | ||
// UI | // UI | ||
function appendMsg(role, content){ | function appendMsg(role, content){ | ||
const div = document.createElement('div'); | const div=document.createElement('div'); | ||
div.className = 'mpai-msg ' + (role === 'user' ? 'user' : | div.className='mpai-msg '+(role==='user'?'user':role==='assistant'?'assistant':'error'); | ||
div.innerHTML = '<div class="role">'+(role==='user'?'Tu':role==='assistant'?'GPT':'Errore')+'</div>' | div.innerHTML='<div class="role">'+(role==='user'?'Tu':role==='assistant'?'GPT':'Errore')+'</div>' | ||
+ '<div class="content" style="white-space:pre-wrap">'+content+'</div>'; | |||
chatEl.appendChild(div); | chatEl.appendChild(div); chatEl.scrollTop=chatEl.scrollHeight; | ||
} | } | ||
function saveLocal(){ if(!currentProject) return; localStorage.setItem('mpai.hist.'+currentProject, JSON.stringify(history.slice(-200))); } | |||
function loadLocal(project){ | function loadLocal(project){ | ||
const raw = localStorage.getItem('mpai.hist.'+project); | const raw=localStorage.getItem('mpai.hist.'+project); | ||
history = raw ? JSON.parse(raw) : []; | history = raw?JSON.parse(raw):[]; | ||
chatEl.innerHTML = ''; | chatEl.innerHTML=''; | ||
if (!history.length){ | if(!history.length){ appendMsg('assistant','Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.'); } | ||
else { history.forEach(m=>appendMsg(m.role,m.content)); } | |||
} | } | ||
function renderFiles(){ | function renderFiles(){ | ||
fileListEl.innerHTML = ''; | fileListEl.innerHTML=''; | ||
attachments.forEach((a,i)=>{ | attachments.forEach((a,i)=>{ | ||
const pill = document.createElement('span'); | const pill=document.createElement('span'); | ||
pill.className = 'mpai-filepill'; | pill.className='mpai-filepill'; | ||
pill.innerHTML = '<span title="Allegato">'+a.name+'</span><button title="Rimuovi">✕</button>'; | pill.innerHTML='<span title="Allegato">'+a.name+'</span><button title="Rimuovi">✕</button>'; | ||
pill.querySelector('button').onclick = ()=>{ attachments.splice(i,1); renderFiles(); }; | pill.querySelector('button').onclick=()=>{ attachments.splice(i,1); renderFiles(); }; | ||
fileListEl.appendChild(pill); | fileListEl.appendChild(pill); | ||
}); | }); | ||
} | } | ||
// | // Sessioni locali | ||
function ensureSession(){ | function ensureSession(){ | ||
if (!sessionId){ | if(!sessionId){ | ||
sessionId = 'sess-' + Date.now(); | sessionId='sess-'+Date.now(); | ||
sessionMeta = { title: 'Nuova conversazione', updated: Date.now() }; | sessionMeta={title:'Nuova conversazione',updated:Date.now()}; | ||
localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | ||
} | } | ||
} | } | ||
function saveSession(){ | function saveSession(){ | ||
if (!sessionId) return; | if(!sessionId) return; | ||
localStorage.setItem('mpai.session.hist.'+sessionId, JSON.stringify(history.slice(-200))); | |||
sessionMeta.updated=Date.now(); | |||
sessionMeta.updated = Date.now(); | |||
localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | ||
} | } | ||
function loadSession(id){ | function loadSession(id){ | ||
sessionId = id; | sessionId=id; | ||
const | const raw=localStorage.getItem('mpai.session.hist.'+id); | ||
history=raw?JSON.parse(raw):[]; | |||
history = raw ? JSON.parse(raw) : []; | const metaRaw=localStorage.getItem('mpai.session.meta.'+id); | ||
const metaRaw = localStorage.getItem('mpai.session.meta.'+id); | sessionMeta=metaRaw?JSON.parse(metaRaw):{title:'Nuova conversazione',updated:Date.now()}; | ||
sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()}; | chatEl.innerHTML=''; | ||
chatEl.innerHTML = ''; | if(!history.length){ appendMsg('assistant','Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.'); } | ||
if (!history.length){ | else { history.forEach(m=>appendMsg(m.role,m.content)); } | ||
} | } | ||
function resetToNewChat(){ | function resetToNewChat(){ | ||
history = []; | history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; | ||
if(currentProject){ appendMsg('assistant','Nuova chat per "'+currentProject+'".'); saveLocal(); } | |||
else { sessionId=null; sessionMeta=null; ensureSession(); appendMsg('assistant','Nuova conversazione.'); saveSession(); } | |||
if (currentProject){ | |||
} | } | ||
// | // Progetti | ||
async function loadProjects(){ | async function loadProjects(){ | ||
const box = $('#mpai-project-list'); | const box=$('#mpai-project-list'); | ||
box.innerHTML = '<em class="muted">Carico progetti…</em>'; | box.innerHTML='<em class="muted">Carico progetti…</em>'; | ||
try{ | try{ | ||
const r = await fetch('/dashboard/api/project_list.php', {cache:'no-store', credentials:'include'}); | const r=await fetch('/dashboard/api/project_list.php',{cache:'no-store',credentials:'include'}); | ||
const j = await r.json(); | const j=await r.json(); | ||
const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []); | const arr=Array.isArray(j)?j:(Array.isArray(j.projects)?j.projects:[]); | ||
if (!arr.length){ box.innerHTML='<em class="muted">Nessun progetto</em>'; return; } | if(!arr.length){ box.innerHTML='<em class="muted">Nessun progetto</em>'; return; } | ||
box.innerHTML=''; | box.innerHTML=''; | ||
arr.forEach(item=>{ | arr.forEach(item=>{ | ||
const name = (typeof item === 'string') ? item : (item.name || item); | const name=(typeof item==='string')?item:(item.name||item); | ||
const row = document.createElement('div'); | const row=document.createElement('div'); | ||
row.className = 'row'; | row.className='row'; | ||
row.innerHTML = '<span class="title">'+name+'</span> | row.innerHTML='<span class="title">'+name+'</span><span><button class="mpai-btn" data-open="'+name+'">Apri</button></span>'; | ||
row.querySelector('[data-open]').onclick=()=>{ currentProject=name; loadLocal(name); }; | |||
row.querySelector('[data-open]').onclick = ()=>{ currentProject=name; loadLocal(name); }; | |||
box.appendChild(row); | box.appendChild(row); | ||
}); | }); | ||
if (!currentProject){ | if(!currentProject){ currentProject=(typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]); loadLocal(currentProject); } | ||
}catch(e){ box.innerHTML='<span class="muted">Errore caricamento: '+e.message+'</span>'; } | |||
}catch(e){ | |||
} | } | ||
async function createProject(){ | async function createProject(){ | ||
const inp = $('#mpai-project-name'); | const inp=$('#mpai-project-name'); | ||
const name = (inp.value||'').trim(); | const name=(inp.value||'').trim(); | ||
if (!name){ alert('Inserisci un nome progetto'); return; } | if(!name){ alert('Inserisci un nome progetto'); return; } | ||
try{ | try{ | ||
const r = await fetch('/dashboard/api/project_create.php', { | const r=await fetch('/dashboard/api/project_create.php',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'include',body:JSON.stringify({name})}); | ||
const txt=await r.text(); let j; try{ j=JSON.parse(txt); }catch{ j={ok:false,raw:txt}; } | |||
if(j.ok!==false){ await loadProjects(); currentProject=name; history=[]; loadLocal(name); inp.value=''; } | |||
const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={ok:false,raw:txt}; } | |||
if (j.ok!==false){ await loadProjects(); currentProject=name; history=[]; loadLocal(name); inp.value=''; } | |||
else { alert('Errore creazione: '+(j.error||'')); } | else { alert('Errore creazione: '+(j.error||'')); } | ||
}catch(e){ alert('Errore rete: '+e.message); } | }catch(e){ alert('Errore rete: '+e.message); } | ||
} | } | ||
// | // Upload (solo elenco locale) | ||
const dz = $('#mpai-dropzone') | const dz=$('#mpai-dropzone'), fi=$('#mpai-file-input'); | ||
dz.addEventListener('dragover',e=>{ e.preventDefault(); dz.style.opacity=.85; }); | |||
dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; }); | dz.addEventListener('dragleave',()=>{ dz.style.opacity=1; }); | ||
dz.addEventListener('dragleave', () => { dz.style.opacity = 1; }); | dz.addEventListener('drop',e=>{ | ||
dz.addEventListener('drop', e => { | e.preventDefault(); dz.style.opacity=1; | ||
e.preventDefault(); dz.style.opacity = 1; | Array.from(e.dataTransfer.files||[]).forEach(f=>attachments.push({file:f,name:f.name})); | ||
Array.from(e.dataTransfer.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | |||
renderFiles(); | renderFiles(); | ||
}); | }); | ||
fi.addEventListener('change', () => { | fi.addEventListener('change',()=>{ | ||
Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | Array.from(fi.files||[]).forEach(f=>attachments.push({file:f,name:f.name})); | ||
fi.value=''; renderFiles(); | fi.value=''; renderFiles(); | ||
}); | }); | ||
// | // Invio prompt (PROXY) | ||
async function sendPrompt(){ | |||
const ta=$('#mpai-input'); | |||
async function sendPrompt(){ | const model=($('#mpai-model')?.value||'gpt-4o-2024-05-13').trim(); | ||
const temp=parseFloat($('#mpai-temp')?.value||'0.7')||0.7; | |||
const sanitizedOnly=$('#mpai-sanitized-only')?.checked; | |||
const content=(ta.value||'').trim(); | |||
if(!content){ ta.focus(); return; } | |||
if(!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); } | |||
history.push({role:'user',content}); appendMsg('user',content); ta.value=''; | |||
const contextName = currentProject ? `Progetto: ${currentProject}` : `Sessione: ${sessionMeta?.title||'Nuova conversazione'}`; | |||
const fileNames = attachments.map(a=>a.name).join(', '); | |||
const preface = `${contextName} | |||
File allegati${sanitizedOnly?' (sanificati)':''}: ${fileNames||'nessuno'} | |||
File allegati${sanitizedOnly ? ' (sanificati)' : ''}: ${fileNames || 'nessuno'} | |||
Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}. | Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}. | ||
| Riga 372: | Riga 291: | ||
${content}`; | ${content}`; | ||
const pending=document.createElement('div'); | |||
pending.className='mpai-msg'; | |||
pending.innerHTML='<em class="muted">Elaboro…</em>'; | |||
chatEl.appendChild(pending); chatEl.scrollTop=chatEl.scrollHeight; | |||
try{ | |||
const r=await fetch('/dashboard/api/openai_project_gpt.php',{ | |||
method:'POST', | |||
headers:{'Content-Type':'application/json'}, | |||
credentials:'include', | |||
cache:'no-store', | |||
body:JSON.stringify({model,prompt:preface}) | |||
}); | |||
const raw=await r.text(); | |||
if(!r.ok){ pending.remove(); appendMsg('error',`HTTP ${r.status} ${r.statusText}\n\n${raw||'(nessun body)'}`); return; } | |||
let j; try{ j=JSON.parse(raw); }catch(e){ pending.remove(); appendMsg('error','Risposta non in JSON:\n\n'+raw.slice(0,1500)); return; } | |||
pending.remove(); | |||
if(j.status==='ok' && j.result){ | |||
if(!currentProject && sessionMeta && sessionMeta.title==='Nuova conversazione'){ sessionMeta.title=(content.slice(0,48)||'Nuova conversazione'); saveSession(); } | |||
history.push({role:'assistant',content:j.result}); appendMsg('assistant',j.result); saveLocal(); | |||
}else{ | |||
const err=j.error||'Errore sconosciuto'; | |||
appendMsg('error',`API error: ${err}\n\nRAW:\n${(j.raw||'').slice(0,1500)}`); | |||
} | |||
}catch(e){ pending.remove(); appendMsg('error','Errore di rete/JS: '+e.message); } | |||
} | } | ||
// Bind | |||
document.addEventListener('click',e=>{ | |||
if(e.target && e.target.id==='mpai-send') sendPrompt(); | |||
if(e.target && e.target.id==='mpai-project-create') createProject(); | |||
if(e.target && e.target.id==='mpai-new-chat') resetToNewChat(); | |||
if(e.target && e.target.id==='mpai-clear'){ | |||
if(!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); } | |||
if(!confirm('Svuotare la conversazione locale?')) return; | |||
resetToNewChat(); | |||
} | |||
}); | |||
document.addEventListener('keydown',e=>{ | |||
if((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$('#mpai-input'); if(ta && ta===document.activeElement){ e.preventDefault(); sendPrompt(); } } | |||
}); | |||
// Boot | |||
loadProjects(); | |||
if(!currentProject){ ensureSession(); loadSession(sessionId); } | |||
})(); | |||
</script> | |||
<!-- | <!-- ========== SEZIONI LEGACY (nascoste) ========== --> | ||
<div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | <div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | ||
<strong>Connessione API (protetta dal server)</strong><br><br> | <strong>Connessione API (protetta dal server)</strong><br><br> | ||
| Riga 447: | Riga 351: | ||
<button onclick="(async()=>{try{const r=await fetch('/dashboard/api/openai_project_gpt.php',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'include',body:JSON.stringify({model:document.getElementById('model-select').value,prompt:document.getElementById('test-prompt').value})}); const j=await r.json(); document.getElementById('api-result').textContent=JSON.stringify(j,null,2);}catch(e){document.getElementById('api-result').textContent=e.message;}})()">▶️ Esegui</button> | <button onclick="(async()=>{try{const r=await fetch('/dashboard/api/openai_project_gpt.php',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'include',body:JSON.stringify({model:document.getElementById('model-select').value,prompt:document.getElementById('test-prompt').value})}); const j=await r.json(); document.getElementById('api-result').textContent=JSON.stringify(j,null,2);}catch(e){document.getElementById('api-result').textContent=e.message;}})()">▶️ Esegui</button> | ||
<pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> | <pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> | ||
</div> | </div> | ||
</html> | </html> | ||
Versione delle 07:50, 28 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup