Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica Etichetta: Annullato |
Nessun oggetto della modifica Etichetta: Ripristino manuale |
||
| Riga 1: | Riga 1: | ||
<html> | <html> | ||
< | <h2>🔧 Dashboard Operativa – Masticationpedia</h2> | ||
<p><em>Centro di comando per progetti, API, file e backup</em></p> | |||
< | <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> | ||
<button onclick="clearServerLog()">🧹 Svuota Log</button> | |||
</ | |||
<div class=" | <!-- 🔘 Pulsanti di navigazione --> | ||
< | <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> | ||
<button class=" | <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="uploadToOpenAI()">📄 Carica in OpenAI</button> | |||
</div> | </div> | ||
<!-- | <!-- 📦 Connessione API (via server, nessuna key lato client) --> | ||
<div | <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> | ||
< | <label>Modello</label> | ||
< | <select id="model-select" style="width:100%; margin-bottom:0.5rem;"> | ||
<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> | |||
<label>Prompt di test</label> | |||
<textarea id="test-prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea><br> | |||
<button onclick="testAPIConnection()">▶️ Esegui</button> | |||
<pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> | |||
</div> | </div> | ||
<!-- | <!-- 📊 Stato Progetti --> | ||
<div id="project-status" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | |||
<strong>📊 Stato Progetti</strong><br><br> | |||
<div id=" | |||
<strong> | <!-- 🧠 Risposta GPT – Analisi progetto --> | ||
<div | <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> | |||
< | <!-- Parametri per dare contesto a GPT --> | ||
<div id="gptParams" style="margin-top:14px;padding:10px;border:1px dashed #ccc;border-radius:8px;background:#fcfcfc;"> | |||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;"> | |||
<input id="p_goal" placeholder="🎯 Obiettivo (es. mini-corso AI)" /> | |||
<input id="p_audience" placeholder="👥 Pubblico (es. medici, studenti)" /> | |||
<input id="p_deliverable" placeholder="📦 Output atteso (es. 6 capitoli + quiz)" /> | |||
<input id="p_constraints" placeholder="⏱️ Vincoli (tempo, budget, tool)" /> | |||
</div> | |||
<div style="margin-top:10px;"> | |||
<button id="btnAnalyze" style="background:#0a7cff;color:#fff;border:none;padding:.5rem .9rem;border-radius:8px;font-weight:600;"> | |||
Analizza con GPT | |||
</button> | |||
</div> | |||
</div> | </div> | ||
</div> | </div> | ||
<div class=" | |||
< | <!-- 📂 Gestione Progetti --> | ||
< | <div class="projects-controls" style="margin-top:1.5rem; padding:1rem; border:1px solid #ddd; border-radius:6px; background:#fff;"> | ||
<h4>📂 Gestione Progetti</h4> | |||
<input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)" style="margin-right:6px; padding:4px;"> | |||
<button id="btnCreateProject" style="background:#28a745; color:white; border:none; padding:0.3rem 0.8rem; border-radius:4px;">Crea progetto</button> | |||
<ul id="projectsList" class="projects-list" style="margin-top:1rem; list-style:none; padding:0;"></ul> | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- | <!-- 🤖 ChatGPT plus (unico contenitore) --> | ||
<div style="margin- | <div id="chatgpt-plus" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fcfcfc;"> | ||
<label>< | <strong>ChatGPT plus – Generazione capitoli</strong><br><br> | ||
<div id=" | |||
<!-- Barra superiore --> | |||
</ | <div style="display:flex; gap:12px; flex-wrap:wrap; margin-bottom:10px;"> | ||
<label>📁 Progetto: | |||
<select id="mpChatProject"> | |||
<option value="Generazione_capitoli" selected>Generazione capitoli</option> | |||
</select> | |||
</label> | |||
<label>⚙️ Modalità: | |||
<select id="mpChatMode"> | |||
<option value="analysis">Analisi</option> | |||
<option value="rewrite">Riscrittura</option> | |||
<option value="generate">Genera capitolo</option> | |||
<option value="biblio">Bibliografia</option> | |||
</select> | |||
</label> | |||
</div> | |||
<!-- Allegati --> | |||
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:8px;"> | |||
<input id="mpChatFile" type="file" multiple /> | |||
<span id="mpChatDrop" style="border:1px dashed #bbb; padding:6px 10px; border-radius:8px; cursor:pointer; user-select:none;"> | |||
Trascina qui file (solo elenco, non inviati) | |||
</span> | |||
</div> | |||
<div id="mpChatFiles" style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px;"></div> | |||
<!-- Input domanda --> | |||
<label><b>Domanda</b> (breve, come in ChatGPT):</label> | |||
<div style="display:flex; gap:8px; align-items:flex-start; margin-bottom:8px;"> | |||
<textarea id="mpChatPrompt" rows="3" style="flex:1 1 auto; width:100%;"></textarea> | |||
<button id="mpChatSend">Invia</button> | |||
</div> | </div> | ||
<!-- | <!-- Output risposta --> | ||
<div | <label><b>Risposta</b> (box grande):</label> | ||
<div | <div id="mpChatAnswer" style="min-height:220px; border:1px solid #ddd; background:#fff; border-radius:8px; padding:10px; white-space:pre-wrap;"></div> | ||
< | |||
< | <!-- Explorer semplice dei file salvati --> | ||
<div style="margin-top:10px; border-top:1px dashed #ddd; padding-top:10px;"> | |||
<div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;"> | |||
<strong>🗂️ File salvati (output/)</strong> | |||
<button id="mpChatRefresh" | |||
title="Aggiorna elenco" | |||
onclick="window.__forceRefreshList && window.__forceRefreshList()">Aggiorna</button> | |||
</div> | </div> | ||
<div | <div id="mpSavedList" style="max-height:180px; overflow:auto; border:1px solid #eee; background:#fff; border-radius:8px; padding:6px; font-family:monospace; font-size:12px;"> | ||
< | <em style="color:#777;">Nessun file elencato. Clicca “Aggiorna”.</em> | ||
</div> | </div> | ||
</div> | </div> | ||
<!-- | <!-- Azioni --> | ||
<div | <div style="margin-top:8px; display:flex; gap:8px; justify-content:flex-end;"> | ||
< | <button id="mpChatClear">Pulisci</button> | ||
< | <button id="mpChatCopy">Copia risposta</button> | ||
<button id="mpChatSave">📥 Salva risposta</button> | |||
</div> | |||
</div> | |||
<!-- 📁 Importa file dal server (semplificata) --> | |||
<div id="serverFileSimpleImportContainer"> | |||
</ | <div class="dashboard-box" style="margin-top:1rem;"> | ||
<h4>📁 Importa file dal server (semplificata)</h4> | |||
<label for="sourcePathInput"><b>📂 Percorso Assoluto del file sorgente:</b></label><br> | |||
<input type="text" id="sourcePathInput" placeholder="/percorso/assoluto/file.php" style="width:100%; margin-bottom:0.5rem;"><br> | |||
<label for="projectNameInput"><b>📁 Cartella di destinazione nel progetto:</b></label><br> | |||
<input type="text" id="projectNameInput" placeholder="nome_progetto/"> | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- =========== | <!-- 📁 Caricamento File GPT --> | ||
<div style="margin-top:2rem;"> | |||
<h3>📁 Carica File GPT</h3> | |||
<div id="test-tools | <label>📁 Project: <input type="text" id="gpt-load-project" value="SSO_LinkedIn" /></label> | ||
<strong>Console JS</strong> | <label>📂 Subfolder: <input type="text" id="gpt-load-subfolder" value="php" /></label> | ||
<textarea id="codeArea" placeholder="Scrivi codice JS da | <label>📄 Filename: <input type="text" id="gpt-load-filename" placeholder="es: testGPT.php" /></label> | ||
<button onclick="caricaFileGPT()">📁 Carica file</button> | |||
</div> | |||
<!-- 🧪 Console JS --> | |||
<pre id="consoleOutput | <div id="test-tools" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | ||
<strong>Console JS:</strong><br> | |||
<textarea id="codeArea" placeholder="Scrivi codice JS da testare..." style="width:100%; height:100px;"></textarea><br> | |||
<button onclick="checkCodeAI()">🔍 Controlla con AI</button> | |||
<pre id="consoleOutput" style="background:#000; color:#0f0; padding:1rem; border-radius:8px; font-family:monospace;"></pre> | |||
</div> | </div> | ||
<!-- | <!-- 📘 Registro Attività --> | ||
<div id="activity-log" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fdfdfd;"> | |||
<strong>📘 Registro attività:</strong> | |||
<div id="activity-log | <button onclick="clearActivityLog()" style="float:right; background:#e74c3c; color:white; border:none; padding:0.4rem 1rem; border-radius:6px; font-weight:bold;">🧹 Svuota</button> | ||
<div id="activityLogContent" style="margin-top:1rem; max-height:250px; overflow-y:auto; background:#f0f0f0; padding:1rem; border:1px solid #ccc; border-radius:6px; font-family:monospace; font-size:0.85rem;"> | |||
<em><span style="color:#888;">Registro avviato...</span></em> | |||
<div id="activityLogContent | |||
<em | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- ===================== | <!-- ✅ Carica JS esterno (se lo usi) --> | ||
<script src="/dashboard/api/dashboard.js?v=20250907d"></script> | |||
<!-- ✅ Mini-script che POPOLA l’elenco e gestisce “Apri” --> | |||
<script> | |||
document.addEventListener('DOMContentLoaded', () => { | |||
const refreshBtn = document.getElementById('mpChatRefresh'); | |||
const listBox = document.getElementById('mpSavedList'); | |||
const answerBox = document.getElementById('mpChatAnswer'); | |||
const projectSel = document.getElementById('mpChatProject'); | |||
function currentProject(){ | |||
return (projectSel?.value || 'Generazione_capitoli').trim(); | |||
} | |||
async function refreshList(){ | |||
if (!listBox) return; | |||
listBox.innerHTML = '<em style="color:#777;">Aggiorno…</em>'; | |||
const prj = currentProject(); | |||
try { | |||
const r = await fetch('/dashboard/api/list_saved_output.php?project=' + encodeURIComponent(prj) + '&sub=output', { cache:'no-store' }); | |||
const txt = await r.text(); | |||
let j; | |||
try { j = JSON.parse(txt); } | |||
catch(e){ | |||
listBox.innerHTML = '<span style="color:#c00;">Risposta non-JSON</span><pre style="white-space:pre-wrap">'+txt.substring(0,400)+'</pre>'; | |||
console.error('list_saved_output NON JSON:', txt); | |||
return; | |||
} | |||
if (!j.ok) { listBox.textContent = 'Errore: ' + (j.error || ''); return; } | |||
if (!j.files || !j.files.length) { listBox.innerHTML = '<em style="color:#777;">Nessun file salvato.</em>'; return; } | |||
listBox.innerHTML = j.files.map(f => ` | |||
<div style="display:flex;justify-content:space-between;gap:8px;border-bottom:1px dashed #eee;padding:4px 0;"> | |||
<span>${f.name} <span style="color:#888;">(${f.size} B)</span></span> | |||
<button class="mpOpen" data-name="${f.name}">Apri</button> | |||
</div> | |||
`).join(''); | |||
listBox.querySelectorAll('.mpOpen').forEach(btn => { | |||
btn.addEventListener('click', async () => { | |||
const name = btn.getAttribute('data-name'); | |||
try { | |||
const rr = await fetch('/dashboard/api/read_saved_output.php', { | |||
method:'POST', | |||
headers:{'Content-Type':'application/json'}, | |||
body: JSON.stringify({ project: prj, file: name }) | |||
}); | |||
const jj = await rr.json(); | |||
if (!jj.ok) { alert('Errore lettura: ' + (jj.error||'')); return; } | |||
if (answerBox) answerBox.textContent = jj.content || ''; | |||
} catch (e) { | |||
alert('Errore apertura file: ' + e.message); | |||
} | |||
}); | |||
}); | |||
} catch (e) { | |||
listBox.textContent = 'Errore JS: ' + e.message; | |||
console.error(e); | |||
} | |||
} | |||
refreshBtn && refreshBtn.addEventListener('click', refreshList); | |||
// carica subito l'elenco alla prima apertura | |||
refreshList(); | |||
}); | |||
</script> | |||
<script> | <script> | ||
/ | // ✅ Correttore AI – funzione GLOBALE chiamata dal bottone "🔍 Controlla con AI" | ||
function | window.checkCodeAI = async function () { | ||
const codeEl = document.getElementById('codeArea'); | |||
const outEl = document.getElementById('consoleOutput'); | |||
if (!codeEl || !outEl) { alert('Manca #codeArea o #consoleOutput'); return; } | |||
} | const code = codeEl.value; | ||
const language = 'php'; // 🔁 cambia a "js", "python", "html", ecc. quando serve | |||
if (!code.trim()) { alert('Incolla del codice da analizzare.'); return; } | |||
outEl.textContent = '⏳ Invio al correttore AI…'; | |||
try { | |||
const r = await fetch('/dashboard/api/ai_check.php', { | |||
try{ | method: 'POST', | ||
const r = await fetch('/dashboard/api/ | headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, | ||
body: new URLSearchParams({ language, code }) | |||
}); | |||
const j = await r.json(); | const j = await r.json(); | ||
if (j.ok){ | |||
if (j.ok) { | |||
let txt = ''; | |||
if (j.lint && j.lint.trim()) { | |||
}else{ | txt += '=== LINT ' + language.toUpperCase() + " ===\n" + j.lint + "\n\n"; | ||
} | |||
txt += (j.analysis || 'Nessun output.'); | |||
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; | ||
} | |||
}; | |||
</script> | |||
/* | <script> | ||
function renderGpt(text){ | /* === Renderer stile ChatGPT: trasforma testo in HTML arioso === */ | ||
function renderGpt(text) { | |||
const box = document.getElementById('gptResponse'); | const box = document.getElementById('gptResponse'); | ||
if (!box) return; | if (!box) return; | ||
if (!text){ box.innerHTML = '<em | if (!text) { box.innerHTML = '<em style="opacity:.7">Nessuna risposta</em>'; return; } | ||
// headings ###, ##, # | |||
let html = (text || '').replace(/\r\n/g, '\n') | |||
.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*#\s?(.*)$/gm, '<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>') | |||
.replace(/^\s*-\s+(.*)$/gm, '<li>$1</li>'); | |||
// racchiudi sequenze di <li> in <ul> | |||
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=>{ | |||
// paragrafi (evita di wrappare headings/list) | |||
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:8px 0;">${line}</p>`; | return `<p style="margin:8px 0;">${line}</p>`; | ||
}).join(''); | }).join(''); | ||
box.innerHTML = html; | box.innerHTML = html; | ||
} | } | ||
/* | /* === Costruzione contesto per GPT dai campi pagina === */ | ||
function | function buildProjectContext() { | ||
const | const title = (document.getElementById('newProjectTitle')?.value || '').trim(); | ||
const | const notes = (document.getElementById('newProjectNotes')?.value || '').trim(); | ||
const | const goal = (document.getElementById('p_goal')?.value || '').trim(); | ||
const | const audience = (document.getElementById('p_audience')?.value || '').trim(); | ||
const deliverable = (document.getElementById('p_deliverable')?.value || '').trim(); | |||
const constraints = (document.getElementById('p_constraints')?.value || '').trim(); | |||
return { title, notes, goal, audience, deliverable, constraints }; | |||
} | |||
function buildPromptForGPT() { | |||
const c = buildProjectContext(); | |||
const out = []; | const out = []; | ||
out.push( | out.push(`You are a senior editor and project designer. Provide a clear, actionable plan.`); | ||
if ( | if (c.title) out.push(`\n# Title\n${c.title}`); | ||
if ( | if (c.notes) out.push(`\n# Notes\n${c.notes}`); | ||
if ( | if (c.goal) out.push(`\n# Goal\n${c.goal}`); | ||
if (c) out.push( | if (c.audience) out.push(`\n# Audience\n${c.audience}`); | ||
out.push( | if (c.deliverable) out.push(`\n# Deliverables\n${c.deliverable}`); | ||
return out.join(' | if (c.constraints) out.push(`\n# Constraints\n${c.constraints}`); | ||
out.push(`\n# Output format | |||
- Executive Summary | |||
- Strengths & Risks | |||
- Step-by-step Plan (milestones) | |||
- Resources & Budget hints | |||
- Metrics of success`); | |||
return out.join('\n'); | |||
} | } | ||
/* | /* === Azione: Analizza con GPT (usa la tua API già protetta) === | ||
async function runProjectAnalysis(){ | * Se NON hai ancora un endpoint dedicato, per test immediato simuliamo la risposta. | ||
* Quando vorrai la chiamata reale, sostituisci la parte "FAKE RESPONSE" con una fetch al tuo endpoint server (/dashboard/api/ai_project_analyze.php). | |||
*/ | |||
async function runProjectAnalysis() { | |||
const btn = document.getElementById('btnAnalyze'); | const btn = document.getElementById('btnAnalyze'); | ||
if (!btn) return; | |||
btn.disabled = true; btn.textContent = 'Analizzo…'; | btn.disabled = true; btn.textContent = 'Analizzo…'; | ||
try{ | |||
try { | |||
const prompt = buildPromptForGPT(); | const prompt = buildPromptForGPT(); | ||
// === FAKE RESPONSE PER TEST VISIVO (subito) === | |||
const fake = `# Executive Summary | |||
Progetto focalizzato su ${ (document.getElementById('newProjectTitle')?.value || 'il tema indicato') }. | |||
Obiettivo: ${ document.getElementById('p_goal')?.value || 'non specificato' }. | |||
Pubblico: ${ document.getElementById('p_audience')?.value || 'non specificato' }. | |||
const | |||
## Strengths & Risks | |||
- Strength: contenuti specialistici. | |||
- Risk: mancano vincoli chiari (${ document.getElementById('p_constraints')?.value || '—' }). | |||
## Step-by-step Plan (milestones) | |||
- M1: Definizione indice capitoli. | |||
- M2: Sviluppo contenuti. | |||
- M3: Revisione clinica e bibliografia. | |||
- M4: Pubblicazione. | |||
## Resources & Budget hints | |||
- Autori, revisori, tool AI, hosting video. | |||
## Metrics of success | |||
- Capitoli completati, feedback medici, engagement studenti.`; | |||
renderGpt(fake); | |||
// === VERSIONE REALE (quando pronta): | |||
// const r = await fetch('/dashboard/api/ai_project_analyze.php', { | |||
// method: 'POST', | |||
// headers: {'Content-Type':'application/x-www-form-urlencoded'}, | |||
// body: new URLSearchParams({ prompt }) | |||
// }); | |||
// const j = await r.json(); | |||
// if (!j.ok) throw new Error(j.error || 'AI error'); | |||
// renderGpt(j.text); | |||
} catch (e) { | |||
renderGpt('❌ Errore: ' + e.message); | |||
} finally { | |||
}catch(e){ | btn.disabled = false; btn.textContent = 'Analizza con GPT'; | ||
} | } | ||
} | } | ||
/* bind del bottone */ | |||
document.addEventListener('DOMContentLoaded', () => { | |||
const b = document.getElementById('btnAnalyze'); | |||
if (b) b.addEventListener('click', runProjectAnalysis); | |||
/* | |||
document.addEventListener('DOMContentLoaded', ()=>{ | |||
}); | }); | ||
</script> | </script> | ||
</html> | </html> | ||
Versione delle 11:00, 21 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup