Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 6: | Riga 6: | ||
<button onclick="clearServerLog()">🧹 Svuota Log</button> | <button onclick="clearServerLog()">🧹 Svuota Log</button> | ||
<!-- 🔘 Pulsanti | <!-- 🔘 Pulsanti --> | ||
<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> | ||
| Riga 14: | Riga 14: | ||
<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" onclick="uploadToOpenAI()">📄 Carica in OpenAI</button> | <button class="dashboard-toggle" onclick="uploadToOpenAI()">📄 Carica in OpenAI</button> | ||
<button class="dashboard-toggle" onclick="showMpAI()">🤖 Masticationpedia AI</button> | <button class="dashboard-toggle" onclick="showMpAI()">🤖 Masticationpedia AI</button> | ||
</div> | </div> | ||
| Riga 48: | Riga 46: | ||
<!-- Chat centrale --> | <!-- Chat centrale --> | ||
<main class="mpai-col mpai-center"> | <main class="mpai-col mpai-center"> | ||
<!-- Allegati --> | <!-- Allegati (solo elenco locale; invio server lo aggiungiamo in step 2) --> | ||
<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 103: | Riga 101: | ||
<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 | --primary:#0a7cff;--danger:#e74c3c;--accent:#eef2ff; | ||
font-family: system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; color:var(--text);} | 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} | ||
| Riga 146: | Riga 144: | ||
</style> | </style> | ||
<!-- ============ JS ( | <!-- ============ JS (tutto qui) ============ --> | ||
<script> | <script> | ||
/* --- mostra la | /* -------- Fallback utili (non rompersi se mancano) -------- */ | ||
function toggleDashboardBox(id){ | |||
var el = document.getElementById(id); | |||
if (!el) return; | |||
// nascondo AI se apro altro box | |||
var ai = document.getElementById('mpAI'); if (ai) ai.style.display='none'; | |||
el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none'; | |||
} | |||
async function clearServerLog(){ | |||
try{ | |||
const r = await fetch('/dashboard/api/log_clear.php',{method:'POST',credentials:'include'}); | |||
const j = await r.json().catch(()=>({ok:false})); | |||
alert(j && j.ok ? 'Log svuotato.' : 'Impossibile svuotare il log.'); | |||
}catch(e){ alert('Errore rete: '+e.message); } | |||
} | |||
function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); } | |||
/* --- mostra la nuova UI AI e nasconde gli altri box --- */ | |||
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(){ | (function(){ | ||
// | // Stato | ||
let currentProject = null; | let currentProject = null; | ||
let attachments = []; // [{file, name}] | let attachments = []; // [{file, name}] | ||
let history = []; // [{role:'user'|'assistant'|'error', content:string}] | let history = []; // [{role:'user'|'assistant'|'error', content:string}] | ||
// | // Shortcuts | ||
const $ = s => document.querySelector(s); | const $ = s => document.querySelector(s); | ||
const chatEl = $('#mpai-chat'); | const chatEl = $('#mpai-chat'); | ||
const fileListEl = $('#mpai-file-list'); | const fileListEl = $('#mpai-file-list'); | ||
// UI helpers | |||
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' : (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>`; | <div class="content" style="white-space:pre-wrap">${content}</div>`; | ||
chatEl.appendChild(div); | chatEl.appendChild(div); | ||
chatEl.scrollTop = chatEl.scrollHeight; | chatEl.scrollTop = chatEl.scrollHeight; | ||
} | } | ||
function saveLocal(){ | function saveLocal(){ | ||
if (!currentProject) return; | if (!currentProject) return; | ||
localStorage.setItem('mpai.hist.'+currentProject, JSON.stringify(history.slice(-200))); | |||
} | } | ||
function loadLocal(project){ | function loadLocal(project){ | ||
const | 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.'); | appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.'); | ||
} else { | } else { | ||
history.forEach(m => appendMsg(m.role, m.content)); | 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'); | |||
pill.className = 'mpai-filepill'; | |||
pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`; | |||
pill.querySelector('button').onclick = ()=>{ attachments.splice(i,1); renderFiles(); }; | |||
fileListEl.appendChild(pill); | |||
}); | |||
} | } | ||
// | // 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); | ||
| Riga 223: | Riga 233: | ||
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>`; | <span><button class="mpai-btn" data-open="${name}">Apri</button></span>`; | ||
row.querySelector('[data-open]').onclick = ()=> { | 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){ | |||
} catch(e){ | |||
box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>'; | box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>'; | ||
} | } | ||
| Riga 244: | Riga 247: | ||
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' }, | headers:{'Content-Type':'application/json'}, | ||
credentials: 'include', | credentials:'include', | ||
body: JSON.stringify({name}) | body: JSON.stringify({name}) | ||
}); | }); | ||
const txt = await r.text(); | 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||'')); } | ||
}catch(e){ alert('Errore rete: '+e.message); } | |||
} catch(e){ | |||
} | } | ||
// | // Upload locale | ||
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; }); | dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; }); | ||
dz.addEventListener('dragleave', | 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})); | |||
renderFiles(); | renderFiles(); | ||
}); | }); | ||
fi.addEventListener('change', | fi.addEventListener('change', () => { | ||
Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | |||
fi.value=''; renderFiles(); | |||
fi.value = ''; | |||
}); | }); | ||
// | // Invio prompt | ||
async function sendPrompt(){ | async function sendPrompt(){ | ||
const ta = $('#mpai-input'); | const ta = $('#mpai-input'); | ||
| Riga 289: | Riga 279: | ||
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; } | ||
| Riga 298: | Riga 287: | ||
ta.value=''; | ta.value=''; | ||
const fileNames = attachments.map(a => a.name).join(', '); | const fileNames = attachments.map(a=>a.name).join(', '); | ||
const preface = `Contesto progetto: ${currentProject} | const preface = `Contesto progetto: ${currentProject} | ||
File allegati${sanitizedOnly?' (sanificati)':''}: ${fileNames || 'nessuno'} | File allegati${sanitizedOnly?' (sanificati)':''}: ${fileNames || 'nessuno'} | ||
| Riga 308: | Riga 297: | ||
const pending = document.createElement('div'); | const pending = document.createElement('div'); | ||
pending.className = 'mpai-msg'; | pending.className='mpai-msg'; pending.innerHTML='<em class="muted">Elaboro…</em>'; | ||
chatEl.appendChild(pending); chatEl.scrollTop=chatEl.scrollHeight; | |||
chatEl.appendChild(pending); | |||
try{ | try{ | ||
| Riga 320: | Riga 307: | ||
body: JSON.stringify({ model, prompt: preface }) | body: JSON.stringify({ model, prompt: preface }) | ||
}); | }); | ||
const txt = await r.text(); | 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){ | |||
if (j.status === 'ok' && j.result){ | |||
history.push({role:'assistant', content:j.result}); | history.push({role:'assistant', content:j.result}); | ||
appendMsg('assistant', j.result); | appendMsg('assistant', j.result); saveLocal(); | ||
}else{ | |||
} 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(); | pending.remove(); history.push({role:'error', content:e.message}); appendMsg('error', e.message); saveLocal(); | ||
} | } | ||
} | } | ||
// | // Bind | ||
document. | document.addEventListener('click', (e)=>{ | ||
if (e.target && e.target.id==='mpai-send') sendPrompt(); | |||
if (e. | if (e.target && e.target.id==='mpai-project-create') createProject(); | ||
if (e.target && e.target.id==='mpai-new-chat'){ | |||
if (!currentProject){ alert('Crea o seleziona un progetto'); return; } | |||
history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Nuova chat per "'+currentProject+'".'); saveLocal(); | |||
} | |||
if (e.target && e.target.id==='mpai-clear'){ | |||
if (!currentProject){ alert('Crea o seleziona un progetto'); return; } | |||
if (!confirm('Svuotare la conversazione locale?')) return; | |||
history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Conversazione pulita.'); saveLocal(); | |||
} | |||
}); | }); | ||
document | 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 ( | |||
}); | }); | ||
// | // Boot | ||
loadProjects(); | loadProjects(); | ||
})(); | })(); | ||
| Riga 376: | Riga 348: | ||
<!-- ========== SEZIONI LEGACY (restano, ma sono nascoste di default) ========== --> | |||
<!-- 📦 Connessione API --> | <!-- 📦 Connessione API --> | ||
<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> | ||
<label>Modello</label> | <label>Modello</label> | ||
<select id="model-select" style="width:100%; margin-bottom:0.5rem;"> | <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-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> | <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> | ||
</select> | </select> | ||
<label>Prompt di test</label> | <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> | <textarea id="test-prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea><br> | ||
<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=" | |||
<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> | ||
| Riga 399: | Riga 367: | ||
<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 style="margin-top: 1rem;"> | <div style="margin-top: 1rem;"> | ||
<label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | ||
<div id="gptResponse" class="gpt-card" style=" | <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> | <em style="opacity:.7">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em> | ||
</div> | </div> | ||
<div id="gptParams" style="margin-top:14px;padding:10px;border:1px dashed #ccc;border-radius:8px;background:#fcfcfc;"> | <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;"> | <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;"> | ||
| Riga 419: | Riga 380: | ||
</div> | </div> | ||
<div style="margin-top:10px;"> | <div style="margin-top:10px;"> | ||
<button id="btnAnalyze" style="background:#0a7cff;color:#fff;border:none;padding:.5rem .9rem;border-radius:8px;font-weight:600;" | <button id="btnAnalyze" style="background:#0a7cff;color:#fff;border:none;padding:.5rem .9rem;border-radius:8px;font-weight:600;" | ||
Analizza con GPT | onclick="(function(){const c={goal:document.getElementById('p_goal').value,audience:document.getElementById('p_audience').value,deliverable:document.getElementById('p_deliverable').value,constraints:document.getElementById('p_constraints').value}; const fake=`# Sommario introduttivo | ||
Obiettivo: ${c.goal||'—'} | |||
Pubblico: ${c.audience||'—'} | |||
## Step | |||
- Pianificazione | |||
- Esecuzione | |||
- Revisione | |||
## Output atteso | |||
${c.deliverable||'—'} | |||
## Vincoli | |||
${c.constraints||'—'}`; (function renderGpt(t){const box=document.getElementById('gptResponse'); if(!box){return} let html=(t||'').replace(/\r\n/g,'\n').replace(/^### (.*)$/gm,'<h3 style=\'margin:14px 0 6px;font-size:17px;\'>$1</h3>').replace(/^## (.*)$/gm,'<h2 style=\'margin:16px 0 8px;font-size:19px;\'>$1</h2>').replace(/^# (.*)$/gm,'<h1 style=\'margin:18px 0 10px;font-size:21px;\'>$1</h1>').replace(/^\- (.*)$/gm,'<li>$1</li>'); html=html.replace(/(<li>.*<\/li>\n?)+/gs, m=>`<ul style=\'margin:8px 0 14px 22px;\'>${m}</ul>`); html=html.split('\n').map(line=>{ if(/^<h\d|^<ul|^<li|^<\/ul>/.test(line)) return line; if(line.trim()==='') return ''; return `<p style='margin:6px 0;line-height:1.5'>${line}</p>`; }).join(''); box.innerHTML=html; })(fake); })()">Analizza con GPT</button> | |||
</div> | </div> | ||
</div> | </div> | ||
</div> | </div> | ||
<div class="projects-controls" style="margin-top:1.5rem; padding:1rem; border:1px solid #ddd; border-radius:6px; background:#fff;"> | <div class="projects-controls" style="margin-top:1.5rem; padding:1rem; border:1px solid #ddd; border-radius:6px; background:#fff;"> | ||
<h4>📂 Gestione Progetti</h4> | <h4>📂 Gestione Progetti</h4> | ||
<input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)" style="margin-right:6px; padding:4px;"> | <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> | <button id="btnCreateProject" style="background:#28a745; color:white; border:none; padding:0.3rem 0.8rem; border-radius:4px;" | ||
onclick="(async()=>{const name=(document.getElementById('newProjectName').value||'').trim(); if(!name){alert('Inserisci un nome'); 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 t=await r.text(); alert(t.includes('ok')?'Creato':'Vedi log/console');}catch(e){alert(e.message)}})()">Crea progetto</button> | |||
<ul id="projectsList" class="projects-list" style="margin-top:1rem; list-style:none; padding:0;"></ul> | <ul id="projectsList" class="projects-list" style="margin-top:1rem; list-style:none; padding:0;"></ul> | ||
</div> | </div> | ||
| Riga 438: | Riga 411: | ||
<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> | ||
<button onclick=" | <button onclick="(function(){const box=document.getElementById('activityLogContent'); if(box) box.textContent=''; })()" 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;"> | <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> | <em><span style="color:#888;">Registro avviato...</span></em> | ||
| Riga 444: | Riga 417: | ||
</div> | </div> | ||
</html> | </html> | ||
Versione delle 14:29, 27 set 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup