Nota: dopo aver pubblicato, potrebbe essere necessario pulire la cache del proprio browser per vedere i cambiamenti.
- Firefox / Safari: tieni premuto il tasto delle maiuscole Shift e fai clic su Ricarica, oppure premi Ctrl-F5 o Ctrl-R (⌘-R su Mac)
- Google Chrome: premi Ctrl-Shift-R (⌘-Shift-R su un Mac)
- Edge: tieni premuto il tasto Ctrl e fai clic su Aggiorna, oppure premi Ctrl-F5.
/* dashboard.js — Masticationpedia (colla UI) */
// ---------- Utilità ----------
const $ = sel => document.querySelector(sel);
const $$ = sel => Array.from(document.querySelectorAll(sel));
function logActivity(msg) {
const box = $('#activityLogContent');
if (!box) return;
const now = new Date();
const hh = now.toTimeString().slice(0,8);
const line = `[${hh}] ${msg}`;
const pre = document.createElement('div');
pre.textContent = line;
box.prepend(pre);
}
async function postJSON(url, body) {
const r = await fetch(url, {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify(body || {})
});
// alcuni endpoint tornano text; gestisco entrambe
const txt = await r.text();
try { return JSON.parse(txt); } catch { return {ok:false, raw:txt}; }
}
// ---------- Toggle sezioni ----------
window.toggleDashboardBox = function(id){
// chiudi tutte
['api-settings','project-status','chatgpt-plus','test-tools','activity-log'].forEach(i=>{
const el = document.getElementById(i);
if (el) el.style.display = 'none';
});
// apri quella scelta
const box = document.getElementById(id);
if (box) box.style.display = 'block';
};
// ---------- Clear log server ----------
window.clearServerLog = async function(){
try {
const r = await fetch('/dashboard/api/log_clear.php', {cache:'no-store'});
const j = await r.json();
if (j.ok) {
alert('Log svuotato ✅');
logActivity('🧹 Log server svuotato');
} else {
alert('Errore svuota log: ' + (j.error||''));
logActivity('❌ Errore svuota log');
}
} catch(e) {
alert('Errore rete: ' + e.message);
}
};
// ---------- Test API OpenAI ----------
window.testAPIConnection = async function(){
const model = ($('#model-select')?.value || 'gpt-4o-2024-05-13').trim();
const prompt = ($('#test-prompt')?.value || 'Dimmi una curiosità sulla mandibola').trim();
logActivity(`🚀 Test API via proxy — Modello: ${model}`);
const res = await postJSON('/dashboard/api/openai_project_gpt.php', {model, prompt});
if (res.status === 'ok' && res.result) {
logActivity(`✅ Proxy OK — Modello: ${model}`);
const out = $('#gptResponse');
if (out) out.textContent = res.result;
} else {
logActivity(`❌ Proxy errore: ${(res.error||res.raw||'sconosciuto')}`);
alert('Errore API: ' + (res.error||res.raw||''));
}
};
// ---------- Analizza progetto (Stato Progetti) ----------
function buildPromptForGPT(){
const goal = ($('#p_goal')?.value||'').trim();
const audience = ($('#p_audience')?.value||'').trim();
const deliverable = ($('#p_deliverable')?.value||'').trim();
const constraints = ($('#p_constraints')?.value||'').trim();
const parts = [];
parts.push('Sei un project manager ed editor senior. Rispondi in ITALIANO. Fornisci un piano chiaro e operativo.');
if (goal) parts.push('\n# Obiettivo\n' + goal);
if (audience) parts.push('\n# Pubblico\n' + audience);
if (deliverable) parts.push('\n# Output atteso\n' + deliverable);
if (constraints) parts.push('\n# Vincoli\n' + constraints);
parts.push(`
# Formato output richiesto
- Sommario introduttivo
- Punti di forza e rischi
- Piano step-by-step (milestones)
- File/Config necessari dal server (ELENCO puntuale dei file da leggere)
- Risorse & Budget hint
- Metriche di successo`);
return parts.join('\n');
}
window.runProjectAnalysis = async function(){
const btn = $('#btnAnalyze');
if (!btn) return;
btn.disabled = true; btn.textContent = 'Analizzo…';
try {
const prompt = buildPromptForGPT();
const model = ($('#model-select')?.value || 'gpt-4o-2024-05-13').trim();
const res = await postJSON('/dashboard/api/openai_project_gpt.php', {model, prompt});
if (res.status === 'ok' && res.result) {
const out = $('#gptResponse');
if (out) out.textContent = res.result;
logActivity('🧠 Analisi GPT completata');
} else {
const msg = res.error || res.raw || 'Errore sconosciuto';
logActivity('❌ Analisi GPT fallita: ' + msg);
alert('Errore analisi: ' + msg);
}
} catch(e){
alert('Errore rete: ' + e.message);
} finally {
btn.disabled = false; btn.textContent = 'Analizza con GPT';
}
};
// ---------- Progetti (crea + lista) ----------
async function refreshProjects(){
const list = $('#projectsList');
if (!list) return;
list.innerHTML = '<em>Carico…</em>';
try {
const r = await fetch('/dashboard/api/project_list.php', {cache:'no-store'});
const j = await r.json();
if (!j.ok || !Array.isArray(j.projects)) { list.textContent = 'Nessun progetto.'; return; }
if (!j.projects.length) { list.textContent = 'Nessun progetto.'; return; }
list.innerHTML = j.projects.map(name => `
<li style="display:flex;gap:8px;align-items:center;margin:4px 0;">
<code>${name}</code>
<button data-open="${name}">Apri</button>
<button data-zip="${name}">ZIP</button>
<button data-del="${name}" title="Sposta nel cestino">🗑️</button>
</li>
`).join('');
list.querySelectorAll('[data-open]').forEach(b=>{
b.addEventListener('click', ()=>selectProject(b.getAttribute('data-open')));
});
list.querySelectorAll('[data-zip]').forEach(b=>{
b.addEventListener('click', ()=>zipProject(b.getAttribute('data-zip')));
});
list.querySelectorAll('[data-del]').forEach(b=>{
b.addEventListener('click', ()=>deleteProject(b.getAttribute('data-del')));
});
} catch(e) {
list.textContent = 'Errore lista: ' + e.message;
}
}
async function selectProject(name){
// eventuale endpoint select (se esiste). Per ora solo log.
logActivity('📂 Project selezionato: ' + name);
}
async function zipProject(name){
try {
const j = await postJSON('/dashboard/api/project_backup.php', { name });
if (j.ok) { alert('ZIP creato: ' + (j.zip||'')); logActivity('🗜️ Backup creato: ' + (j.zip||'')); }
else { alert('Errore ZIP: ' + (j.error||'')); }
} catch(e){ alert('Errore: ' + e.message); }
}
async function deleteProject(name){
if (!confirm('Spostare nel cestino "'+name+'"?')) return;
try {
const j = await postJSON('/dashboard/api/project_delete.php', { name });
if (j.ok) { logActivity('🗑️ Progetto spostato nel cestino'); refreshProjects(); }
else { alert('Errore delete: ' + (j.error||'')); }
} catch(e){ alert('Errore: ' + e.message); }
}
async function createProject(){
const inp = $('#newProjectName');
const name = (inp?.value||'').trim();
if (!name) { alert('Inserisci un nome progetto'); return; }
try {
const j = await postJSON('/dashboard/api/project_create.php', { name });
if (j.ok) {
logActivity('✅ Progetto creato: ' + name);
inp.value = '';
refreshProjects();
} else {
alert('Errore creazione: ' + (j.error||''));
}
} catch(e) {
alert('Errore rete: ' + e.message);
}
}
// ---------- Carica in OpenAI (stub clic pulsante in alto) ----------
window.uploadToOpenAI = function(){
alert('Funzione "Carica in OpenAI" in preparazione.');
};
// ---------- Bind all ----------
document.addEventListener('DOMContentLoaded', ()=>{
// bottoni/azioni principali già definiti in HTML
$('#btnAnalyze') && $('#btnAnalyze').addEventListener('click', window.runProjectAnalysis);
$('#btnCreateProject') && $('#btnCreateProject').addEventListener('click', createProject);
refreshProjects();
});
/* ===================== MPAI – CHAT APP (no inline script) ===================== */
/** Mostra la UI AI e nasconde i box legacy */
window.showMpAI = function(){
['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
.forEach(id => { const el=document.getElementById(id); if(el) el.style.display='none'; });
const ai = document.getElementById('mpAI');
if (ai){ ai.style.display='block'; ai.scrollIntoView({behavior:'smooth'}); }
};
/** Stato locale della chat */
const mpaiState = {
currentProject: null, // “progetto reale” (opzionale)
sessionId: null, // id bozza locale
sessionMeta: null, // {title, updated}
history: [],
attachments: []
};
/** Shortcuts */
const $mp = (s)=>document.querySelector(s);
/** UI helpers */
function mpaiAppendMsg(role, content){
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
const div = document.createElement('div');
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 class="content" style="white-space:pre-wrap">${content}</div>`;
chatEl.appendChild(div);
chatEl.scrollTop = chatEl.scrollHeight;
}
function mpaiRenderFiles(){
const fileListEl = $mp('#mpai-file-list');
if (!fileListEl) return;
fileListEl.innerHTML = '';
mpaiState.attachments.forEach((a,i)=>{
const pill = document.createElement('span');
pill.className = 'mpai-filepill';
pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
pill.querySelector('button').onclick = ()=>{ mpaiState.attachments.splice(i,1); mpaiRenderFiles(); };
fileListEl.appendChild(pill);
});
}
/** Persistenza “per progetto” (cronologia locale) */
function mpaiSaveLocal(){
if (!mpaiState.currentProject) return;
localStorage.setItem('mpai.hist.'+mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200)));
}
function mpaiLoadLocal(project){
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
const raw = localStorage.getItem('mpai.hist.'+project);
mpaiState.history = raw ? JSON.parse(raw) : [];
chatEl.innerHTML = '';
if (!mpaiState.history.length){
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.');
} else {
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
}
}
/** Bozze locali (se non c’è progetto) */
function mpaiEnsureSession(){
if (!mpaiState.sessionId){
mpaiState.sessionId = 'sess-' + Date.now();
mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
localStorage.setItem('mpai.session.meta.'+mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
}
}
function mpaiSaveSession(){
if (!mpaiState.sessionId) return;
const key = 'mpai.session.hist.' + mpaiState.sessionId;
localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200)));
mpaiState.sessionMeta.updated = Date.now();
localStorage.setItem('mpai.session.meta.'+mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
}
function mpaiLoadSession(id){
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
mpaiState.sessionId = id;
const key = 'mpai.session.hist.' + id;
const raw = localStorage.getItem(key);
mpaiState.history = raw ? JSON.parse(raw) : [];
const metaRaw = localStorage.getItem('mpai.session.meta.'+id);
mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()};
chatEl.innerHTML = '';
if (!mpaiState.history.length){
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
} else {
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
}
}
/** Lista progetti (sidebar AI) – opzionale ma comoda */
async function mpaiLoadProjects(){
const box = $mp('#mpai-project-list');
if (!box) return; // se non c’è la UI AI, non fare nulla
box.innerHTML = '<em class="muted">Carico progetti…</em>';
try{
const r = await fetch('/dashboard/api/project_list.php', {cache:'no-store', credentials:'include'});
const j = await r.json();
const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
if (!arr.length){ box.innerHTML='<em class="muted">Nessun progetto</em>'; return; }
box.innerHTML='';
arr.forEach(item=>{
const name = (typeof item === 'string') ? item : (item.name || item);
const row = document.createElement('div');
row.className = 'row';
row.innerHTML = `<span class="title">${name}</span>
<span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
row.querySelector('[data-open]').onclick = ()=>{
mpaiState.currentProject = name;
mpaiLoadLocal(name);
};
box.appendChild(row);
});
if (!mpaiState.currentProject){
const first = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]);
mpaiState.currentProject = first;
mpaiLoadLocal(first);
}
}catch(e){
box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>';
}
}
async function mpaiCreateProject(){
const inp = $mp('#mpai-project-name');
const name = (inp?.value||'').trim();
if (!name){ alert('Inserisci un nome progetto'); return; }
try{
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 mpaiLoadProjects(); mpaiState.currentProject=name; mpaiState.history=[]; mpaiLoadLocal(name); inp.value=''; }
else { alert('Errore creazione: '+(j.error||'')); }
}catch(e){ alert('Errore rete: '+e.message); }
}
/** INVIO PROMPT → OpenAI proxy */
window.sendPrompt = async function(){
const ta = $mp('#mpai-input');
if (!ta) return;
const model = ($mp('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
const temp = parseFloat($mp('#mpai-temp')?.value || '0.7') || 0.7;
const sanitizedOnly = !!($mp('#mpai-sanitized-only')?.checked);
const content = (ta.value || '').trim();
if (!content){ ta.focus(); return; }
// Se non c'è progetto, attiva bozza locale
if (!mpaiState.currentProject && !mpaiState.sessionId){
mpaiEnsureSession();
mpaiLoadSession(mpaiState.sessionId);
}
// Mostra subito il messaggio utente
mpaiState.history.push({role:'user', content});
mpaiAppendMsg('user', content);
ta.value = '';
// Contesto
const contextName = mpaiState.currentProject
? `Progetto: ${mpaiState.currentProject}`
: `Sessione: ${mpaiState.sessionMeta?.title || 'Nuova conversazione'}`;
const fileNames = mpaiState.attachments.map(a => a.name).join(', ');
const preface = `${contextName}
File allegati${sanitizedOnly ? ' (sanificati)' : ''}: ${fileNames || 'nessuno'}
Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}.
---
Utente:
${content}`;
// Placeholder “Elaboro…”
const pending = document.createElement('div');
pending.className = 'mpai-msg';
pending.innerHTML = '<em class="muted">Elaboro…</em>';
const chatEl = $mp('#mpai-chat');
chatEl && chatEl.appendChild(pending);
try{
const r = await fetch('/dashboard/api/openai_project_gpt.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
mode: 'same-origin',
cache: 'no-store',
body: JSON.stringify({ model, prompt: preface })
});
const raw = await r.text();
pending.remove?.();
if (!r.ok){
console.error('GPT proxy error', r.status, r.statusText, raw);
mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${raw || '(nessun body)'}`);
mpaiSaveLocal(); return;
}
let j;
try { j = JSON.parse(raw); }
catch(parseErr){
console.error('JSON parse error', parseErr, raw);
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${raw.slice(0,1500)}`);
mpaiSaveLocal(); return;
}
if (j.status === 'ok' && j.result){
// Se è bozza, rinomina titolo alla prima risposta
if (!mpaiState.currentProject && mpaiState.sessionMeta && mpaiState.sessionMeta.title === 'Nuova conversazione'){
mpaiState.sessionMeta.title = (content.slice(0,48) || 'Nuova conversazione');
mpaiSaveSession();
}
mpaiState.history.push({role:'assistant', content: j.result});
mpaiAppendMsg('assistant', j.result);
mpaiSaveLocal();
} else {
const err = j.error || 'Errore sconosciuto';
mpaiAppendMsg('error', `API error: ${err}\n\nRAW:\n${(j.raw||'').slice(0,1500)}`);
mpaiSaveLocal();
}
} catch(e){
pending.remove?.();
console.error('Network/JS error', e);
mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`);
mpaiSaveLocal();
}
};
/** Bind UI quando la pagina è pronta (solo se la UI AI esiste) */
document.addEventListener('DOMContentLoaded', ()=>{
if (!$mp('#mpAI')) return; // non siamo nella dashboard AI
// Upload locale (solo elenco)
const dz = $mp('#mpai-dropzone');
const fi = $mp('#mpai-file-input');
dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
dz && dz.addEventListener('drop', e => {
e.preventDefault(); dz.style.opacity = 1;
Array.from(e.dataTransfer.files||[]).forEach(f=> mpaiState.attachments.push({file:f,name:f.name}));
mpaiRenderFiles();
});
fi && fi.addEventListener('change', () => {
Array.from(fi.files||[]).forEach(f=> mpaiState.attachments.push({file:f,name:f.name}));
fi.value=''; mpaiRenderFiles();
});
// Bottoni
$mp('#mpai-send')?.addEventListener('click', window.sendPrompt);
$mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject);
$mp('#mpai-new-chat')?.addEventListener('click', ()=>{
mpaiState.history=[]; mpaiState.attachments=[]; mpaiRenderFiles();
const chatEl = $mp('#mpai-chat'); if (chatEl){ chatEl.innerHTML=''; }
if (mpaiState.currentProject){ mpaiAppendMsg('assistant','Nuova chat per "'+mpaiState.currentProject+'".'); mpaiSaveLocal(); }
else { mpaiState.sessionId=null; mpaiState.sessionMeta=null; mpaiEnsureSession(); mpaiAppendMsg('assistant','Nuova conversazione.'); mpaiSaveSession(); }
});
$mp('#mpai-clear')?.addEventListener('click', ()=>{
if (!mpaiState.currentProject){ alert('Crea o seleziona un progetto'); return; }
if (!confirm('Svuotare la conversazione locale?')) return;
mpaiState.history=[]; mpaiState.attachments=[]; mpaiRenderFiles();
const chatEl = $mp('#mpai-chat'); if (chatEl){ chatEl.innerHTML=''; }
mpaiAppendMsg('assistant','Conversazione pulita.'); mpaiSaveLocal();
});
// Invio con Cmd/Ctrl+Enter nella textarea
document.addEventListener('keydown', (e)=>{
if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$mp('#mpai-input'); if (ta && ta===document.activeElement){ e.preventDefault(); window.sendPrompt(); } }
});
// Boot
mpaiLoadProjects();
});
/* ===================== /MPAI – CHAT APP ===================== */