Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica Etichetta: Annullato |
||
| Riga 1: | Riga 1: | ||
<html> | <!doctype html> | ||
<html lang="it"> | |||
<head> | |||
<meta charset="utf-8" /> | |||
<title>🔧 Dashboard Operativa – Masticationpedia</title> | |||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |||
<style> | |||
body{font:15px/1.55 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:#1f2328;background:#fafafa;margin:18px} | |||
h2{margin:.2rem 0 0.6rem 0} | |||
.dashboard-toggle{background:#fff;border:1px solid #ddd;border-radius:10px;padding:.5rem .9rem;cursor:pointer} | |||
.dashboard-box{background:#fdfdfd;border:1px solid #ddd;border-radius:10px;padding:1rem} | |||
input,textarea,select,button{font:inherit} | |||
input,textarea,select{border:1px solid #ccc;border-radius:8px;padding:.45rem .6rem} | |||
button{border:1px solid #ddd;border-radius:8px;background:#fff;cursor:pointer} | |||
button.primary{background:#0a7cff;border-color:#0a7cff;color:#fff} | |||
.gpt-card{background:#fff;border:1px solid #e6e6e6;border-radius:10px;padding:16px;box-shadow:0 1px 2px rgba(0,0,0,.04)} | |||
.muted{color:#666} | |||
.list{max-height:220px;overflow:auto;border:1px solid #eee;border-radius:10px;background:#fff;padding:8px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px} | |||
</style> | |||
</head> | |||
<body> | |||
<h2>🔧 Dashboard Operativa – Masticationpedia</h2> | <h2>🔧 Dashboard Operativa – Masticationpedia</h2> | ||
<p><em>Centro di comando per progetti, API, file e backup</em></p> | <p class="muted"><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> | <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> | ||
<button onclick="clearServerLog()">🧹 Svuota Log</button> | <button onclick="clearServerLog()">🧹 Svuota Log</button> | ||
<!-- 🔘 Pulsanti di navigazione --> | <!-- 🔘 Pulsanti di navigazione --> | ||
<div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: | <div style="margin: 1rem 0 1.2rem 0; display:flex; flex-wrap:wrap; gap:.6rem;"> | ||
<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('project-status')">📊 Stato Progetti</button> | ||
| Riga 19: | Riga 38: | ||
<!-- 📦 Connessione API (via server, nessuna key lato client) --> | <!-- 📦 Connessione API (via server, nessuna key lato client) --> | ||
<div id="api-settings" style="display:none | <div id="api-settings" class="dashboard-box" style="display:none;"> | ||
<strong>Connessione API (protetta dal server)</strong><br><br> | <strong>Connessione API (protetta dal server)</strong><br><br> | ||
| Riga 32: | Riga 51: | ||
<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="testAPIConnection()">▶️ Esegui</button> | <button class="primary" 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> | <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 --> | <!-- 📊 Stato Progetti --> | ||
<div id="project-status" style="display:none; | <div id="project-status" class="dashboard-box" style="display:none; margin-top:1rem;"> | ||
<strong>📊 Stato Progetti</strong><br><br> | <strong>📊 Stato Progetti</strong><br><br> | ||
<!-- 🧠 Risposta GPT – Analisi progetto --> | <!-- 🧠 Risposta GPT – Analisi progetto --> | ||
<div | <div> | ||
<label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label> | ||
<div id="gptResponse" class="gpt-card | <div id="gptResponse" class="gpt-card"> | ||
<em class="muted">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em> | |||
<em | |||
</div> | </div> | ||
<!-- Parametri per dare contesto a GPT --> | <!-- Parametri per dare contesto a GPT --> | ||
<div id="gptParams" style="margin-top: | <div id="gptParams" class="dashboard-box" style="margin-top:12px; border-style:dashed;"> | ||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;"> | <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;"> | ||
<input id="p_goal" placeholder="🎯 Obiettivo (es. | <input id="p_goal" placeholder="🎯 Obiettivo (es. Attivare SSO LinkedIn)" /> | ||
<input id="p_audience" placeholder="👥 Pubblico (es. | <input id="p_audience" placeholder="👥 Pubblico (es. amministratori, dev)" /> | ||
<input id="p_deliverable" placeholder="📦 Output atteso (es. | <input id="p_deliverable" placeholder="📦 Output atteso (es. patch LocalSettings + file PHP)" /> | ||
<input id="p_constraints" placeholder="⏱️ Vincoli ( | <input id="p_constraints" placeholder="⏱️ Vincoli (es. MediaWiki 1.43, niente conflitti)" /> | ||
</div> | </div> | ||
<div style="margin-top:10px;"> | <div style="margin-top:10px;"> | ||
<button id="btnAnalyze" | <button id="btnAnalyze" class="primary">Analizza con GPT</button> | ||
</div> | </div> | ||
</div> | </div> | ||
| Riga 67: | Riga 81: | ||
<!-- 📂 Gestione Progetti --> | <!-- 📂 Gestione Progetti --> | ||
<div class="projects-controls" style="margin-top | <div class="projects-controls dashboard-box" style="margin-top:1rem; background:#fff;"> | ||
<h4>📂 Gestione Progetti</h4> | <h4>📂 Gestione Progetti</h4> | ||
<input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -) | <div style="display:flex;gap:6px;flex-wrap:wrap;"> | ||
<input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)"> | |||
<button id="btnCreateProject" class="primary">Crea progetto</button> | |||
</div> | |||
<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> | ||
</div> | </div> | ||
<!-- 🤖 ChatGPT plus (stato attuale) --> | |||
<!-- 🤖 ChatGPT plus ( | <div id="chatgpt-plus" class="dashboard-box" style="display:none; margin-top:1rem;"> | ||
<div id="chatgpt-plus" style="display:none; | |||
<strong>ChatGPT plus – Generazione capitoli</strong><br><br> | <strong>ChatGPT plus – Generazione capitoli</strong><br><br> | ||
| Riga 126: | Riga 140: | ||
onclick="window.__forceRefreshList && window.__forceRefreshList()">Aggiorna</button> | onclick="window.__forceRefreshList && window.__forceRefreshList()">Aggiorna</button> | ||
</div> | </div> | ||
<div id="mpSavedList" | <div id="mpSavedList" class="list"> | ||
<em style="color:#777;">Nessun file elencato. Clicca “Aggiorna”.</em> | <em style="color:#777;">Nessun file elencato. Clicca “Aggiorna”.</em> | ||
</div> | </div> | ||
| Riga 140: | Riga 154: | ||
<!-- 📁 Importa file dal server (semplificata) --> | <!-- 📁 Importa file dal server (semplificata) --> | ||
<div id="serverFileSimpleImportContainer" | <div id="serverFileSimpleImportContainer" class="dashboard-box" style="margin-top:1rem;"> | ||
<h4>📁 Importa file dal server (semplificata)</h4> | |||
<!-- Nuovo bottone: Copia sanificato → progetto --> | |||
< | <div style="margin:.4rem 0 .6rem 0;"> | ||
<label for=" | <button class="primary" onclick="copyFromServerSanitized()">🔐 Copia (sanificato) → progetto</button> | ||
<input type="text" id=" | <small class="muted" style="display:block;margin-top:6px;"> | ||
Compila i campi in basso (Project/Subfolder/Filename) per decidere la destinazione nel progetto. | |||
</small> | |||
</div> | |||
<div style="display:grid;grid-template-columns:1fr;gap:6px;max-width:820px;"> | |||
<label for="sourcePathInput"><b>📂 Percorso Assoluto del file sorgente (SERVER):</b></label> | |||
<input type="text" id="sourcePathInput" placeholder="/var/www/html/masticationpedia-staging/LocalSettings.php" /> | |||
</div> | </div> | ||
</div> | </div> | ||
<!-- 📁 Caricamento File GPT --> | <!-- 📁 Caricamento/lettura File GPT (destinazione) --> | ||
<div style="margin-top: | <div class="dashboard-box" style="margin-top:1rem;"> | ||
<h3>📁 Carica File GPT</h3> | <h3>📁 Carica/Leggi File GPT</h3> | ||
<label>📁 Project: <input type="text" id="gpt-load-project" value=" | <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;"> | ||
<label>📁 Project: <input type="text" id="gpt-load-project" value="Test_Dashboard_OK" /></label> | |||
<label>📂 Subfolder: <input type="text" id="gpt-load-subfolder" value="php" /></label> | |||
<label>📄 Filename: <input type="text" id="gpt-load-filename" placeholder="es: LocalSettings.php" /></label> | |||
<button onclick="caricaFileGPT()">📖 Leggi file</button> | |||
</div> | |||
</div> | </div> | ||
<!-- 🧪 Console JS --> | <!-- 🧪 Console JS --> | ||
<div id="test-tools" style="display:none; | <div id="test-tools" class="dashboard-box" style="display:none; margin-top:1rem;"> | ||
<strong>Console JS:</strong><br> | <strong>Console JS:</strong><br> | ||
<textarea id="codeArea" placeholder="Scrivi codice JS da testare..." style="width:100%; height:100px;"></textarea><br> | <textarea id="codeArea" placeholder="Scrivi codice JS da testare..." style="width:100%; height:100px;"></textarea><br> | ||
<button onclick="checkCodeAI()">🔍 Controlla con AI</button> | <button onclick="checkCodeAI()">🔍 Controlla con AI</button> | ||
<pre id="consoleOutput" style="background:#000; color:#0f0; padding:1rem; border-radius:8px; font-family:monospace;"></pre> | <pre id="consoleOutput" style="background:#000; color:#0f0; padding:1rem; border-radius:8px; font-family:monospace;"></pre> | ||
</div> | </div> | ||
<!-- 📘 Registro Attività --> | <!-- 📘 Registro Attività --> | ||
<div id="activity-log" style="display:none; | <div id="activity-log" class="dashboard-box" style="display:none; margin-top:1rem;"> | ||
<strong>📘 Registro attività:</strong> | <strong>📘 Registro attività:</strong> | ||
<button onclick="clearActivityLog()" style="float:right; background:#e74c3c; color:white; border:none; padding:0.4rem 1rem; border-radius:6px; font-weight:bold;">🧹 Svuota</button> | <button onclick="clearActivityLog()" style="float:right; background:#e74c3c; color:white; border:none; padding:0.4rem 1rem; border-radius:6px; font-weight:bold;">🧹 Svuota</button> | ||
| Riga 179: | Riga 202: | ||
<script src="/dashboard/api/dashboard.js?v=20250907d"></script> | <script src="/dashboard/api/dashboard.js?v=20250907d"></script> | ||
<!-- ============ JS inline ============ --> | |||
<!-- | |||
<script> | <script> | ||
/* UI base */ | |||
const | function toggleDashboardBox(id){ | ||
const el = document.getElementById(id); | |||
if (!el) return; | |||
el.style.display = (el.style.display === 'block' ? 'none' : 'block'); | |||
} | |||
/* Log: svuota lato server (log_clear.php già configurato) */ | |||
async function clearServerLog(){ | |||
if (!confirm('Svuotare il Log Dashboard?')) return; | |||
try{ | |||
const r = await fetch('/dashboard/api/log_clear.php', { method:'POST', credentials:'include' }); | |||
const j = await r.json(); | |||
if (j.ok){ | |||
alert('✅ Log svuotato'); | |||
window.open('/dashboard/api/log_view.php?n=300', '_blank'); | |||
}else{ | |||
alert('❌ Errore: '+(j.error||'operazione fallita')); | |||
} | } | ||
} | }catch(e){ alert('❌ Errore rete: '+e.message); } | ||
} | |||
/* API proxy test (già esistente lato server) */ | |||
async function testAPIConnection(){ | |||
try { | const prompt = (document.getElementById('test-prompt')?.value||'').trim(); | ||
const | const output = document.getElementById('api-result'); | ||
method: 'POST', | const model = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13'; | ||
if (!prompt){ output.innerText='⚠️ Inserisci un prompt di test.'; return; } | |||
body: | output.innerText='⏳ Contatto il proxy sul server...'; | ||
try{ | |||
const res = await fetch('/dashboard/api/openai_project_gpt.php', { | |||
method:'POST', headers:{'Content-Type':'application/json'}, credentials:'include', | |||
body: JSON.stringify({ prompt, model }) | |||
}); | }); | ||
const | const data = await res.json(); | ||
if (data.status==='ok' && data.result){ | |||
if ( | output.innerText = '✅ Risposta: '+data.result; | ||
} else { | } else { | ||
output.innerText = '❌ Errore: ' + (data.error || 'Risposta vuota'); | |||
} | } | ||
} catch (e) { | }catch(e){ output.innerText='🚫 Errore di rete: '+e.message; } | ||
} | |||
} | |||
/* Renderer stile ChatGPT per la risposta GPT */ | |||
/* | function renderGpt(text){ | ||
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 class="muted">Nessuna risposta</em>'; return; } | ||
let html = (text||'').replace(/\r\n/g,'\n') | |||
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*#{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*#{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, | .replace(/^\s*-\s+(.*)$/gm, '<li>$1</li>'); | ||
.replace(/^\s*-\s+(.*)$/gm, | |||
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=>{ | |||
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: | return `<p style="margin:6px 0;">${line}</p>`; | ||
}).join(''); | }).join(''); | ||
box.innerHTML = html; | box.innerHTML = html; | ||
} | } | ||
/* | /* Costruzione prompt da parametri progetto */ | ||
function buildProjectContext() { | function buildProjectContext(){ | ||
const goal = (document.getElementById('p_goal')?.value||'').trim(); | |||
const audience = (document.getElementById('p_audience')?.value||'').trim(); | |||
const goal = (document.getElementById('p_goal')?.value || '').trim(); | const deliverable = (document.getElementById('p_deliverable')?.value||'').trim(); | ||
const audience = (document.getElementById('p_audience')?.value || '').trim(); | const constraints = (document.getElementById('p_constraints')?.value||'').trim(); | ||
const deliverable = (document.getElementById('p_deliverable')?.value || '').trim(); | return {goal,audience,deliverable,constraints}; | ||
const constraints = (document.getElementById('p_constraints')?.value || '').trim(); | |||
return { | |||
} | } | ||
function buildPromptForGPT(){ | |||
function buildPromptForGPT() { | |||
const c = buildProjectContext(); | const c = buildProjectContext(); | ||
const out = []; | const out = []; | ||
out.push( | out.push('Rispondi in ITALIANO. Sei un architetto software MediaWiki+PHP.'); | ||
out.push('\n# Output atteso\n- Executive Summary\n- Rischi e mitigazioni\n- Piano step-by-step (milestones)\n- File da toccare/creare\n- Test e rollback'); | |||
if (c.goal) out.push('\n# Obiettivo\n'+c.goal); | |||
if (c.goal) | if (c.audience) out.push('\n# Pubblico\n'+c.audience); | ||
if (c.audience) | if (c.deliverable) out.push('\n# Deliverable\n'+c.deliverable); | ||
if (c.deliverable) out.push( | if (c.constraints) out.push('\n# Vincoli\n'+c.constraints); | ||
if (c.constraints) out.push( | |||
return out.join('\n'); | return out.join('\n'); | ||
} | } | ||
/* | /* Analisi con GPT via proxy (usa endpoint già esistente) */ | ||
async function runProjectAnalysis(){ | |||
async function runProjectAnalysis() { | |||
const btn = document.getElementById('btnAnalyze'); | const btn = document.getElementById('btnAnalyze'); | ||
if (!btn) return; | if (!btn) return; | ||
btn.disabled = true; btn.textContent = 'Analizzo…'; | btn.disabled = true; btn.textContent='Analizzo…'; | ||
try{ | |||
try { | |||
const prompt = buildPromptForGPT(); | const prompt = buildPromptForGPT(); | ||
const res = await fetch('/dashboard/api/openai_project_gpt.php', { | |||
method:'POST', headers:{'Content-Type':'application/json'}, credentials:'include', | |||
body: JSON.stringify({ prompt, model: (document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13') }) | |||
}); | |||
const j = await res.json(); | |||
if (j.status==='ok' && j.result) renderGpt(j.result); | |||
else renderGpt('❌ Errore: '+(j.error||'Risposta vuota')); | |||
}catch(e){ renderGpt('❌ Errore rete: '+e.message); } | |||
finally{ btn.disabled=false; btn.textContent='Analizza con GPT'; } | |||
} | |||
/* Crea progetto (usa API server già presenti) */ | |||
async function createProject(){ | |||
const input = document.getElementById('newProjectName'); | |||
const name = (input.value||'').trim(); | |||
if (!name) return alert('Inserisci un nome progetto (A-Z 0-9 _ -)'); | |||
try{ | |||
const body = new URLSearchParams({ name }); | |||
const r = await fetch('/dashboard/api/project_create.php', { | |||
method:'POST', body, credentials:'include' | |||
}); | |||
const j = await r.json(); | |||
if (!j.ok) throw new Error(j.error||'create failed'); | |||
input.value=''; | |||
await loadProjects(); | |||
alert('✅ Progetto creato: '+name); | |||
}catch(e){ alert('❌ Errore creazione: '+e.message); } | |||
} | |||
/* Lista e rendering progetti */ | |||
- | async function loadProjects(){ | ||
const ul = document.getElementById('projectsList'); if (!ul) return; | |||
ul.innerHTML = '<em class="muted">Carico…</em>'; | |||
try{ | |||
const r = await fetch('/dashboard/api/project_list.php', {credentials:'include',cache:'no-store'}); | |||
const j = await r.json(); | |||
if (!j.ok) throw new Error(j.error||'list failed'); | |||
const items = (j.projects||[]).sort((a,b)=> (b.mtime||0)-(a.mtime||0)); | |||
if (!items.length){ ul.innerHTML = '<em class="muted">Nessun progetto. Creane uno.</em>'; return; } | |||
ul.innerHTML = ''; | |||
items.forEach(p=>{ | |||
const li = document.createElement('li'); | |||
li.style.display='flex'; li.style.alignItems='center'; li.style.gap='8px'; li.style.margin='6px 0'; | |||
const label = document.createElement('span'); label.textContent = p.name; label.style.fontWeight='600'; | |||
const open = document.createElement('button'); open.textContent='Apri'; | |||
open.onclick = ()=> alert('Selezionato: '+p.name+' (vista file arriverà dopo)'); | |||
const zip = document.createElement('button'); zip.textContent='ZIP'; zip.title='Crea backup zip'; | |||
zip.onclick = async ()=>{ | |||
const body = new URLSearchParams({ name: p.name }); | |||
const rr = await fetch('/dashboard/api/project_backup.php', {method:'POST', body, credentials:'include'}); | |||
const jj = await rr.json(); if (!jj.ok) return alert('❌ Errore backup: '+(jj.error||'')); alert('✅ Backup creato: '+jj.zip); | |||
}; | |||
const del = document.createElement('button'); del.textContent='🗑️'; del.title='Sposta nel cestino'; | |||
del.onclick = async ()=>{ | |||
if (!confirm('Spostare "'+p.name+'" nel cestino?')) return; | |||
const body = new URLSearchParams({ name: p.name }); | |||
const rr = await fetch('/dashboard/api/project_delete.php', {method:'POST', body, credentials:'include'}); | |||
const jj = await rr.json(); if (!jj.ok) return alert('❌ Errore eliminazione: '+(jj.error||'')); loadProjects(); | |||
}; | |||
li.append(label, open, zip, del); | |||
ul.appendChild(li); | |||
}); | |||
}catch(e){ ul.innerHTML = '<span style="color:#c00">Errore: '+e.message+'</span>'; } | |||
} | |||
/* 📁 COPIA DAL SERVER → PROGETTO (SANIFICATO) */ | |||
async function copyFromServerSanitized(){ | |||
- | const src = (document.getElementById('sourcePathInput')?.value||'').trim(); | ||
- | const project = (document.getElementById('gpt-load-project')?.value||'').trim(); | ||
- | const subfolder = (document.getElementById('gpt-load-subfolder')?.value||'php').trim() || 'php'; | ||
const filename = (document.getElementById('gpt-load-filename')?.value||'').trim(); | |||
if (!src) return alert('Inserisci il percorso assoluto del file sorgente (SERVER).'); | |||
if (!project) return alert('Inserisci il nome del progetto.'); | |||
if (!filename) return alert('Inserisci il Filename di destinazione.'); | |||
const destination = `${project}/${subfolder}/${filename}`; | |||
try{ | |||
const r = await fetch('/dashboard/api/copy_file_from_path.php', { | |||
method:'POST', credentials:'include', | |||
headers:{'Content-Type':'application/json'}, | |||
body: JSON.stringify({ source: src, destination, sanitize: 1 }) // << SANIFICA | |||
}); | |||
const txt = await r.text(); | |||
let j; try{ j=JSON.parse(txt); }catch{ j={ok:false,error:'Risposta non-JSON',raw:txt}; } | |||
if (j.ok){ | |||
alert('✅ Copiato (sanificato) in: '+(j.path||destination)); | |||
} else { | |||
alert('❌ Errore copia: '+(j.error||'')+(j.raw?'\nRAW:\n'+j.raw.substring(0,400):'')); | |||
} | |||
}catch(e){ alert('❌ Errore rete: '+e.message); } | |||
} | |||
/* Lettura file salvato (verifica) */ | |||
async function caricaFileGPT(){ | |||
const project = (document.getElementById('gpt-load-project')?.value||'').trim(); | |||
const subfolder = (document.getElementById('gpt-load-subfolder')?.value||'php').trim()||'php'; | |||
const filename = (document.getElementById('gpt-load-filename')?.value||'').trim(); | |||
if (!project || !filename) return alert('Compila Project e Filename'); | |||
} | try{ | ||
renderGpt('❌ Errore: ' + | const url = `/dashboard/api/read_file.php?projectName=${encodeURIComponent(project)}&subfolder=${encodeURIComponent(subfolder)}&filename=${encodeURIComponent(filename)}`; | ||
} | const r = await fetch(url, {credentials:'include',cache:'no-store'}); | ||
const j = await r.json(); | |||
if (j.status==='ok'){ | |||
renderGpt('```\n'+(j.content||'')+'\n```'); | |||
} else { | |||
alert('❌ Errore lettura: '+(j.error||'')); | |||
} | |||
}catch(e){ alert('❌ Errore rete: '+e.message); } | |||
} | } | ||
/* | /* Bind iniziali */ | ||
document.addEventListener('DOMContentLoaded', () => { | document.addEventListener('DOMContentLoaded', ()=>{ | ||
document.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis); | |||
document.getElementById('btnCreateProject')?.addEventListener('click', createProject); | |||
loadProjects(); | |||
}); | }); | ||
</script> | </script> | ||
</body> | |||
</html> | </html> | ||
Versione delle 10:36, 21 set 2025
<!doctype html>
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup
📁 Importa file dal server (semplificata)
Compila i campi in basso (Project/Subfolder/Filename) per decidere la destinazione nel progetto.