Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 13: | Riga 13: | ||
<button class="dashboard-toggle" onclick="toggleDashboardBox('test-tools')">🧪 Strumenti di Test</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="toggleDashboardBox('activity-log')">📘 Registro Attività</button> | ||
<button class="dashboard-toggle" | <button class="dashboard-toggle" | ||
onclick="(window.showMpAI ? showMpAI() : (function(){var ai=document.getElementById('mpAI'); if(ai) ai.style.display='block';})())"> | |||
🤖 Masticationpedia AI | |||
</button> | |||
</div> | </div> | ||
<script> | <script> | ||
/* | /* ===== Fallback/utility sempre caricati ===== */ | ||
function toggleDashboardBox(id){ | function toggleDashboardBox(id){ | ||
var el = document.getElementById(id); | var el = document.getElementById(id); | ||
if (!el) return; | 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'; | ||
| Riga 185: | Riga 36: | ||
} | } | ||
function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); } | function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); } | ||
window.showMpAI = function(){ | |||
function | |||
['api-settings','project-status','chatgpt-plus','test-tools','activity-log'] | ['api-settings','project-status','chatgpt-plus','test-tools','activity-log'] | ||
.forEach(id | .forEach(function(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 ========= */ | /* ========= MPAI APP ========= */ | ||
| Riga 202: | Riga 51: | ||
let history = []; | let history = []; | ||
let attachments = []; | let attachments = []; | ||
// Shortcuts | // Shortcuts | ||
| Riga 227: | Riga 75: | ||
chatEl.innerHTML = ''; | chatEl.innerHTML = ''; | ||
if (!history.length){ | if (!history.length){ | ||
appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". | appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Puoi allegare file sanificati.'); | ||
} else { | } else { | ||
history.forEach(m => appendMsg(m.role, m.content)); | history.forEach(m => appendMsg(m.role, m.content)); | ||
| Riga 243: | Riga 91: | ||
} | } | ||
/ | /* ===== Sessioni locali (bozze) ===== */ | ||
function ensureSession(){ | |||
function | if (!sessionId){ | ||
sessionId = 'sess-' + Date.now(); | |||
sessionMeta = { title: 'Nuova conversazione', updated: Date.now() }; | |||
localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | |||
} | |||
} | |||
} | function saveSession(){ | ||
if (!sessionId) return; | |||
function | const key = 'mpai.session.hist.' + sessionId; | ||
localStorage.setItem(key, JSON.stringify(history.slice(-200))); | |||
sessionMeta.updated = Date.now(); | |||
sessionMeta = | |||
localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta)); | ||
} | } | ||
function loadSession(id){ | |||
sessionId = id; | |||
const key = 'mpai.session.hist.' + id; | |||
const raw = localStorage.getItem(key); | |||
history = raw ? JSON.parse(raw) : []; | |||
const metaRaw = localStorage.getItem('mpai.session.meta.'+id); | |||
sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()}; | |||
chatEl.innerHTML = ''; | |||
if (!history.length){ | |||
appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.'); | |||
function loadSession(id){ | } else { | ||
history.forEach(m => appendMsg(m.role, m.content)); | |||
} | |||
} | } | ||
function resetToNewChat(){ | |||
history = []; | |||
function resetToNewChat(){ | attachments = []; | ||
renderFiles(); | |||
chatEl.innerHTML = ''; | |||
if (currentProject){ | |||
appendMsg('assistant','Nuova chat per "'+currentProject+'".'); | |||
saveLocal(); | |||
} else { | |||
sessionId = null; sessionMeta = null; | |||
ensureSession(); | |||
appendMsg('assistant','Nuova conversazione.'); | |||
saveSession(); | |||
} | |||
} | } | ||
/* ===== Progetti (lista/crea) ===== */ | |||
// | |||
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 325: | Riga 156: | ||
box.appendChild(row); | box.appendChild(row); | ||
}); | }); | ||
if (!currentProject){ | if (!currentProject){ | ||
const first = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]); | |||
currentProject = first; | |||
loadLocal(first); | |||
} | |||
}catch(e){ | }catch(e){ | ||
box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>'; | box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>'; | ||
| Riga 347: | Riga 182: | ||
} | } | ||
/ | /* ===== Upload locale (visuale) ===== */ | ||
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(); | |||
}); | |||
} | |||
/ | /* ===== Invio prompt (via proxy sicuro) ===== */ | ||
async function sendPrompt(){ | async function sendPrompt(){ | ||
const ta = $('#mpai-input'); | const ta = $('#mpai-input'); | ||
const model = ($('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim(); | const model = ($('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim(); | ||
const temp = parseFloat($('#mpai-temp')?.value || '0.7') || 0.7; | const temp = parseFloat($('#mpai-temp')?.value || '0.7') || 0.7; | ||
const sanitizedOnly = $('#mpai-sanitized-only')?.checked; | const sanitizedOnly = !!($('#mpai-sanitized-only')?.checked); | ||
const content = (ta.value||'').trim(); | |||
const content = (ta.value || '').trim(); | |||
if (!content){ ta.focus(); return; } | if (!content){ ta.focus(); return; } | ||
// Se non c'è un progetto, abilita la bozza locale | |||
if (!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); } | |||
// Mostra subito il messaggio dell'utente | |||
history.push({role:'user', content}); | history.push({role:'user', content}); | ||
appendMsg('user', content); | appendMsg('user', content); | ||
ta.value=''; | ta.value = ''; | ||
// Contesto: progetto o sessione locale | |||
const contextName = currentProject | |||
? `Progetto: ${currentProject}` | |||
: `Sessione: ${sessionMeta?.title || 'Nuova conversazione'}`; | |||
const fileNames = attachments.map(a=>a.name).join(', '); | const fileNames = attachments.map(a => a.name).join(', '); | ||
const preface = ` | 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 385: | Riga 233: | ||
${content}`; | ${content}`; | ||
// Placeholder “Elaboro…” | |||
const pending = document.createElement('div'); | const pending = document.createElement('div'); | ||
pending.className='mpai-msg'; pending.innerHTML='<em class="muted">Elaboro…</em>'; | pending.className = 'mpai-msg'; | ||
chatEl.appendChild(pending); chatEl.scrollTop=chatEl.scrollHeight; | pending.innerHTML = '<em class="muted">Elaboro…</em>'; | ||
chatEl.appendChild(pending); | |||
chatEl.scrollTop = chatEl.scrollHeight; | |||
try{ | try{ | ||
const r = await fetch('/dashboard/api/openai_project_gpt.php', { | const r = await fetch('/dashboard/api/openai_project_gpt.php', { | ||
method:'POST', | method: 'POST', | ||
headers:{'Content-Type':'application/json'}, | headers: { 'Content-Type': 'application/json' }, | ||
credentials:'include', | credentials: 'include', // per Basic Auth | ||
body: JSON.stringify({ model, prompt: preface }) | body: JSON.stringify({ model, prompt: preface }) | ||
}); | }); | ||
const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={status:'error',raw:txt}; } | |||
const txt = await r.text(); | |||
let j; try{ j = JSON.parse(txt); } catch { j = { status:'error', raw: txt }; } | |||
pending.remove(); | pending.remove(); | ||
if (j.status==='ok' && j.result){ | |||
history.push({role:'assistant', content:j.result}); | if (j.status === 'ok' && j.result){ | ||
appendMsg('assistant', j.result); saveLocal(); | if (!currentProject && sessionMeta && sessionMeta.title === 'Nuova conversazione'){ | ||
}else{ | 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'; | const err = j.error || 'Errore sconosciuto'; | ||
history.push({role:'error', content:err}); | history.push({role:'error', content: err}); | ||
appendMsg('error', err + (j.raw?'\n\nRAW:\n'+j.raw:'')); | appendMsg('error', err + (j.raw ? '\n\nRAW:\n' + j.raw : '')); | ||
saveLocal(); | saveLocal(); | ||
} | } | ||
}catch(e){ | } catch(e){ | ||
pending.remove(); history.push({role:'error', content:e.message}); appendMsg('error', e.message); saveLocal(); | pending.remove(); | ||
history.push({role:'error', content: e.message}); | |||
appendMsg('error', e.message); | |||
saveLocal(); | |||
} | } | ||
} | } | ||
// | /* ===== Bind UI ===== */ | ||
document.addEventListener('click', (e)=>{ | document.addEventListener('click', (e)=>{ | ||
if (e.target && e.target.id==='mpai-send') sendPrompt(); | 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-project-create') createProject(); | ||
if (e.target && e.target.id==='mpai-new-chat') | if (e.target && e.target.id === 'mpai-new-chat') resetToNewChat(); | ||
if (e.target && e.target.id === 'mpai-clear') resetToNewChat(); | |||
// (in futuro: mpai-save-as-project) | |||
if (e.target && e.target.id==='mpai-clear') | |||
}); | }); | ||
document.addEventListener('keydown', (e)=>{ | document.addEventListener('keydown', (e)=>{ | ||
if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$('#mpai-input'); if (ta && ta===document.activeElement){ e.preventDefault(); sendPrompt(); } } | if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { | ||
const ta = $('#mpai-input'); | |||
if (ta && ta === document.activeElement) { e.preventDefault(); sendPrompt(); } | |||
} | |||
}); | }); | ||
// | /* ===== Boot ===== */ | ||
loadProjects(); | loadProjects(); | ||
if (!currentProject) { ensureSession(); loadSession(sessionId); } | |||
})(); // chiude l’IIFE | |||
})(); | |||
</script> | </script> | ||
<!-- ============== MASTICATIONPEDIA AI (schermata unica) ============== --> | |||
<div id="mpAI" class="mpai-root" style="display:none"> | |||
<!-- Header --> | |||
<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">💾 Salva come progetto</button> | |||
</div> | |||
</div> | |||
<!-- ============== | <div class="mpai-grid"> | ||
<!-- Sidebar sinistra: Progetti --> | |||
<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> | |||
<!-- Chat centrale --> | |||
<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> | |||
<!-- Sidebar destra: Impostazioni --> | |||
<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 ( | <!-- ========== SEZIONI LEGACY (nascoste di default) ========== --> | ||
<!-- 📦 Connessione API --> | <!-- 📦 Connessione API --> | ||
Versione delle 15:52, 27 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup