Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica Etichetta: Annullato |
Nessun oggetto della modifica Etichetta: Annullato |
||
| Riga 9: | Riga 9: | ||
<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="toggleDashboardBox('project-status')">📊 Stato Progetti</button> | |||
<button class="dashboard-toggle" onclick="toggleDashboardBox('chatgpt-plus')">🤖 ChatGPT plus</button> | |||
<button class="dashboard-toggle" onclick="toggleDashboardBox('test-tools')">🧪 Strumenti di Test</button> | |||
<button class="dashboard-toggle" onclick="toggleDashboardBox('activity-log')">📘 Registro Attività</button> | |||
<button class="dashboard-toggle" onclick="showMpAI()">🤖 Masticationpedia AI</button> | |||
< | |||
< | |||
</div> | </div> | ||
| Riga 160: | Riga 64: | ||
<!-- ============ JS (tutto qui) ============ --> | <!-- ============ JS (tutto qui) ============ --> | ||
<script> | <script> | ||
// | // Utilità base | ||
function toggleDashboardBox(id){ | function toggleDashboardBox(id){ | ||
var el = document.getElementById(id); | var el = document.getElementById(id); | ||
| Riga 174: | Riga 78: | ||
}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'; }); | .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'; | if (ai) ai.style.display='block'; | ||
} | } | ||
/ | // ========= MPAI APP ========= | ||
(function(){ | document.addEventListener('DOMContentLoaded', function(){ | ||
// Stato | // Stato | ||
let currentProject = null; // progetto | let currentProject = null; // progetto server o null | ||
let sessionId = null; // id bozza locale | let sessionId = null; // id bozza locale | ||
let sessionMeta = null; // {title, updated} | let sessionMeta = null; // {title, updated} | ||
let history = []; | let history = []; | ||
| Riga 200: | Riga 104: | ||
const div = document.createElement('div'); | const div = document.createElement('div'); | ||
div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error')); | 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; | chatEl.scrollTop = chatEl.scrollHeight; | ||
| Riga 230: | Riga 136: | ||
} | } | ||
// | // Sessioni locali (bozze) | ||
function ensureSession(){ | function ensureSession(){ | ||
if (!sessionId){ | if (!sessionId){ | ||
| Riga 257: | Riga 163: | ||
} else { | } else { | ||
history.forEach(m => appendMsg(m.role, m.content)); | history.forEach(m => appendMsg(m.role, m.content)); | ||
} | } | ||
} | } | ||
// | // Progetti | ||
async function loadProjects(){ | async function loadProjects(){ | ||
const box = $('#mpai-project-list'); | const box = $('#mpai-project-list'); | ||
if (!box) return; | |||
box.innerHTML = '<em class="muted">Carico progetti…</em>'; | box.innerHTML = '<em class="muted">Carico progetti…</em>'; | ||
try{ | try{ | ||
| Riga 290: | Riga 182: | ||
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); | ||
| Riga 309: | Riga 201: | ||
try{ | try{ | ||
const r = await fetch('/dashboard/api/project_create.php', { | const r = await fetch('/dashboard/api/project_create.php', { | ||
method:'POST', | method:'POST', headers:{'Content-Type':'application/json'}, credentials:'include', | ||
body: JSON.stringify({name}) | body: JSON.stringify({name}) | ||
}); | }); | ||
const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={ok:false,raw:txt}; } | 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=''; | if (j.ok!==false){ | ||
else { alert('Errore creazione: '+(j.error||'')); } | await loadProjects(); currentProject=name; history=[]; loadLocal(name); inp.value=''; | ||
} else { alert('Errore creazione: '+(j.error||'')); } | |||
}catch(e){ alert('Errore rete: '+e.message); } | }catch(e){ alert('Errore rete: '+e.message); } | ||
} | } | ||
// | // Upload locale (solo elenco) | ||
const dz = $('#mpai-dropzone'); | const dz = $('#mpai-dropzone'); | ||
const fi = $('#mpai-file-input'); | const fi = $('#mpai-file-input'); | ||
dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; }); | if (dz){ | ||
dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; }); | |||
dz.addEventListener('dragleave', () => { dz.style.opacity = 1; }); | |||
dz.addEventListener('drop', e => { | |||
e.preventDefault(); dz.style.opacity = 1; | |||
Array.from(e.dataTransfer.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | |||
renderFiles(); | |||
fi.addEventListener('change', () => { | }); | ||
} | |||
if (fi){ | |||
}); | fi.addEventListener('change', () => { | ||
Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | |||
fi.value=''; renderFiles(); | |||
}); | |||
} | |||
// === sendPrompt GLOBALE === | |||
window.sendPrompt = async function(){ | |||
const ta = $('#mpai-input'); | |||
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'} | ||
| Riga 374: | Riga 260: | ||
${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', | |||
body: JSON.stringify({ model, prompt: preface }) | |||
}); | |||
const txt = await r.text(); | |||
let j; try{ j = JSON.parse(txt); } catch { j = { status:'error', raw: txt }; } | |||
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'; | |||
history.push({role:'error', content: err}); | |||
appendMsg('error', err + (j.raw ? '\n\nRAW:\n' + j.raw : '')); | |||
saveLocal(); | |||
} | |||
} catch(e){ | |||
pending.remove(); | |||
history.push({role:'error', content: e.message}); | |||
appendMsg('error', e.message); | |||
saveLocal(); | |||
} | |||
}; | |||
// Bind | |||
document.addEventListener('click', (e)=>{ | |||
if (e.target && e.target.id==='mpai-send') window.sendPrompt(); | |||
if (e.target && e.target.id==='mpai-project-create') createProject(); | |||
if (e.target && e.target.id==='mpai-new-chat'){ | |||
if (!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); } | |||
history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; | |||
appendMsg('assistant', currentProject ? ('Nuova chat per "'+currentProject+'".') : 'Nuova conversazione.'); | |||
if (currentProject) saveLocal(); else saveSession(); | |||
} | |||
if (e.target && e.target.id==='mpai-clear'){ | |||
if (!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); } | |||
if (!confirm('Svuotare la conversazione locale?')) return; | |||
history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; | |||
appendMsg('assistant','Conversazione pulita.'); | |||
if (currentProject) saveLocal(); else saveSession(); | |||
} | |||
}); | |||
document.addEventListener('keydown', (e)=>{ | |||
if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ | |||
const ta=$('#mpai-input'); | |||
if (ta && ta===document.activeElement){ e.preventDefault(); window.sendPrompt(); } | |||
} | |||
}); | |||
// Boot | |||
loadProjects(); | |||
if (!currentProject){ ensureSession(); loadSession(sessionId); } | |||
}); | }); | ||
</script> | |||
<!-- ============== MASTICATIONPEDIA AI (schermata unica) ============== --> | |||
<div id="mpAI" class="mpai-root" style="display:none"> | |||
<div class="mpai-header"> | |||
<div class="mpai-title">🤖 Masticationpedia <b>AI</b></div> | |||
<div class="mpai-actions"> | |||
<button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button> | |||
<button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button> | |||
<button id="mpai-save-as-project" class="mpai-btn" disabled>💾 Salva come progetto</button> | |||
</div> | |||
</div> | |||
// | <div class="mpai-grid"> | ||
<!-- Sinistra --> | |||
<aside class="mpai-col mpai-left"> | |||
<h4>📂 Progetti</h4> | |||
<div id="mpai-project-list" class="mpai-projects"><em class="muted">Carico progetti…</em></div> | |||
<div class="mpai-new-project"> | |||
<input id="mpai-project-name" type="text" placeholder="Nuovo progetto (A-Z 0-9 _ -)" /> | |||
<button id="mpai-project-create" class="mpai-btn">Crea</button> | |||
</div> | |||
<div class="mpai-help muted">Ogni progetto mantiene la sua cronologia locale (browser).</div> | |||
</aside> | |||
<!-- Centro --> | |||
<main class="mpai-col mpai-center"> | |||
<div id="mpai-uploads" class="mpai-uploads" data-state="idle"> | |||
<div class="mpai-dropzone" id="mpai-dropzone"> | |||
<div> | |||
<div class="muted" style="margin-bottom:6px;">Trascina qui file <b>sanificati</b> oppure</div> | |||
<label class="mpai-file-label"> | |||
<input id="mpai-file-input" type="file" multiple style="display:none;"> | |||
<span class="mpai-btn">📂 Aggiungi file</span> | |||
</label> | |||
</div> | |||
</div> | |||
<div id="mpai-file-list" class="mpai-filelist"></div> | |||
</div> | |||
<div id="mpai-chat" class="mpai-chat"> | |||
<div class="mpai-msg mpai-hint"> | |||
Benvenuto. Seleziona un progetto a sinistra, oppure scrivi subito: se non c’è un progetto creo una conversazione locale. | |||
Le risposte appariranno qui sotto in ordine, come in ChatGPT. | |||
</div> | |||
</div> | |||
<div class="mpai-composer"> | |||
<textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> | |||
<button id="mpai-send" class="mpai-btn primary">Invia</button> | |||
</div> | |||
</main> | |||
</ | |||
<!-- Destra --> | |||
<aside class="mpai-col mpai-right"> | |||
<h4>⚙️ Impostazioni</h4> | |||
<label class="mpai-field">Modello | |||
<select id="mpai-model"> | |||
<option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> | |||
<option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> | |||
</select> | |||
</label> | |||
<label class="mpai-field">Temperatura | |||
<input id="mpai-temp" type="number" min="0" max="1" step="0.1" value="0.7"> | |||
</label> | |||
<label class="mpai-check"> | |||
<input id="mpai-sanitized-only" type="checkbox" checked> | |||
Usa solo file <b>sanificati</b> come contesto | |||
</label> | |||
<div class="mpai-help muted">Le API key restano lato server (file sicuro). Il browser non vede mai le chiavi.</div> | |||
</aside> | |||
</div> | |||
</div> | |||
<!-- ========== SEZIONI LEGACY (nascoste di default) ========== --> | <!-- ========== SEZIONI LEGACY (nascoste di default) ========== --> | ||
<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 451: | Riga 417: | ||
</div> | </div> | ||
<div id="project-status" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | <div id="project-status" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | ||
<strong>📊 Stato Progetti</strong><br><br> | <strong>📊 Stato Progetti</strong><br><br> | ||
<div id="gptResponse" class="gpt-card" style="margin-top:8px; background:#fff; border:1px solid #e6e6e6; border-radius:10px; padding:16px; font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; color:#1f2328; box-shadow: 0 1px 2px rgba(0,0,0,.04);"> | <div style="margin-top: 1rem;"> | ||
<label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | |||
<div id="gptResponse" class="gpt-card" style="margin-top:8px; background:#fff; border:1px solid #e6e6e6; border-radius:10px; padding:16px; font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; color:#1f2328; box-shadow: 0 1px 2px rgba(0,0,0,.04);"> | |||
<em style="opacity:.7">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em> | |||
</div> | |||
</div> | </div> | ||
</div> | </div> | ||
<div id="activity-log" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fdfdfd;"> | <div id="activity-log" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fdfdfd;"> | ||
<strong>📘 Registro attività:</strong> | <strong>📘 Registro attività:</strong> | ||
Versione delle 06:59, 28 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup