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: 1rem;">
<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; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;">
<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; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;">
<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 style="margin-top: 1rem;">
   <div>
     <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">
        margin-top:8px; background:#fff; border:1px solid #e6e6e6; border-radius:10px;
       <em class="muted">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em>
        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>


     <!-- Parametri per dare contesto a GPT -->
     <!-- 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 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. mini-corso AI)" />
         <input id="p_goal"        placeholder="🎯 Obiettivo (es. Attivare SSO LinkedIn)" />
         <input id="p_audience"    placeholder="👥 Pubblico (es. medici, studenti)" />
         <input id="p_audience"    placeholder="👥 Pubblico (es. amministratori, dev)" />
         <input id="p_deliverable" placeholder="📦 Output atteso (es. 6 capitoli + quiz)" />
         <input id="p_deliverable" placeholder="📦 Output atteso (es. patch LocalSettings + file PHP)" />
         <input id="p_constraints" placeholder="⏱️ Vincoli (tempo, budget, tool)" />
         <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" style="background:#0a7cff;color:#fff;border:none;padding:.5rem .9rem;border-radius:8px;font-weight:600;">
         <button id="btnAnalyze" class="primary">Analizza con GPT</button>
          Analizza con GPT
        </button>
       </div>
       </div>
     </div>
     </div>
Riga 67: Riga 81:


   <!-- 📂 Gestione Progetti -->
   <!-- 📂 Gestione Progetti -->
   <div class="projects-controls" style="margin-top:1.5rem; padding:1rem; border:1px solid #ddd; border-radius:6px; background:#fff;">
   <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 _ -)" style="margin-right:6px; padding:4px;">
     <div style="display:flex;gap:6px;flex-wrap:wrap;">
    <button id="btnCreateProject" style="background:#28a745; color:white; border:none; padding:0.3rem 0.8rem; border-radius:4px;">Crea progetto</button>
      <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 (unico contenitore) -->
<div id="chatgpt-plus" class="dashboard-box" style="display:none; margin-top:1rem;">
<div id="chatgpt-plus" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fcfcfc;">
   <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" style="max-height:180px; overflow:auto; border:1px solid #eee; background:#fff; border-radius:8px; padding:6px; font-family:monospace; font-size:12px;">
     <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;">
  <div class="dashboard-box" style="margin-top:1rem;">
  <h4>📁 Importa file dal server (semplificata)</h4>
    <h4>📁 Importa file dal server (semplificata)</h4>
 
    <label for="sourcePathInput"><b>📂 Percorso Assoluto del file sorgente:</b></label><br>
  <!-- Nuovo bottone: Copia sanificato → progetto -->
     <input type="text" id="sourcePathInput" placeholder="/percorso/assoluto/file.php" style="width:100%; margin-bottom:0.5rem;"><br>
  <div style="margin:.4rem 0 .6rem 0;">
     <label for="projectNameInput"><b>📁 Cartella di destinazione nel progetto:</b></label><br>
    <button class="primary" onclick="copyFromServerSanitized()">🔐 Copia (sanificato) → progetto</button>
     <input type="text" id="projectNameInput" placeholder="nome_progetto/">
     <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:2rem;">
<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="SSO_LinkedIn" /></label>
   <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
  <label>📂 Subfolder: <input type="text" id="gpt-load-subfolder" value="php" /></label>
    <label>📁 Project: <input type="text" id="gpt-load-project" value="Test_Dashboard_OK" /></label>
  <label>📄 Filename: <input type="text" id="gpt-load-filename" placeholder="es: testGPT.php" /></label>
    <label>📂 Subfolder: <input type="text" id="gpt-load-subfolder" value="php" /></label>
  <button onclick="caricaFileGPT()">📁 Carica file</button>
    <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; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;">
<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; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fdfdfd;">
<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 ============ -->
<!-- ✅ Mini-script che POPOLA l’elenco e gestisce “Apri” -->
<script>
<script>
document.addEventListener('DOMContentLoaded', () => {
/* UI base */
   const refreshBtn = document.getElementById('mpChatRefresh');
function toggleDashboardBox(id){
   const listBox    = document.getElementById('mpSavedList');
   const el = document.getElementById(id);
   const answerBox  = document.getElementById('mpChatAnswer');
   if (!el) return;
  const projectSel = document.getElementById('mpChatProject');
   el.style.display = (el.style.display === 'block' ? 'none' : 'block');
}


  function currentProject(){
/* Log: svuota lato server (log_clear.php già configurato) */
    return (projectSel?.value || 'Generazione_capitoli').trim();
async function clearServerLog(){
  }
  if (!confirm('Svuotare il Log Dashboard?')) return;
 
  try{
  async function refreshList(){
    const r = await fetch('/dashboard/api/log_clear.php', { method:'POST', credentials:'include' });
    if (!listBox) return;
    const j = await r.json();
    listBox.innerHTML = '<em style="color:#777;">Aggiorno…</em>';
    if (j.ok){
    const prj = currentProject();
       alert('✅ Log svuotato');
    try {
       window.open('/dashboard/api/log_view.php?n=300', '_blank');
      const r = await fetch('/dashboard/api/list_saved_output.php?project=' + encodeURIComponent(prj) + '&sub=output', { cache:'no-store' });
    }else{
      const txt = await r.text();
      alert('Errore: '+(j.error||'operazione fallita'));
      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);
     }
     }
   }
   }catch(e){ alert('❌ Errore rete: '+e.message); }
 
}
  refreshBtn && refreshBtn.addEventListener('click', refreshList);
  // carica subito l'elenco alla prima apertura
  refreshList();
});
</script>
<script>
// ✅ Correttore AI – funzione GLOBALE chiamata dal bottone "🔍 Controlla con AI"
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…';
/* API proxy test (già esistente lato server) */
 
async function testAPIConnection(){
   try {
  const prompt = (document.getElementById('test-prompt')?.value||'').trim();
     const r = await fetch('/dashboard/api/ai_check.php', {
  const output = document.getElementById('api-result');
       method: 'POST',
  const model  = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13';
      headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
  if (!prompt){ output.innerText='⚠️ Inserisci un prompt di test.'; return; }
       body: new URLSearchParams({ language, code })
   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 j = await r.json();
     const data = await res.json();
 
     if (data.status==='ok' && data.result){
     if (j.ok) {
      output.innerText = '✅ Risposta: '+data.result;
      let txt = '';
      if (j.lint && j.lint.trim()) {
        txt += '=== LINT ' + language.toUpperCase() + " ===\n" + j.lint + "\n\n";
      }
      txt += (j.analysis || 'Nessun output.');
      outEl.textContent = txt;
     } else {
     } else {
       outEl.textContent = '❌ Errore: ' + (j.error || 'sconosciuto');
       output.innerText = '❌ Errore: ' + (data.error || 'Risposta vuota');
      if (j.raw) outEl.textContent += '\n\nRAW:\n' + j.raw;
     }
     }
   } catch (e) {
   }catch(e){ output.innerText='🚫 Errore di rete: '+e.message; }
    outEl.textContent = 'Errore rete: ' + e.message;
}
  }
};
</script>


<script>
/* Renderer stile ChatGPT per la risposta GPT */
/* === Renderer stile ChatGPT: trasforma testo in HTML arioso === */
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 style="opacity:.7">Nessuna risposta</em>'; return; }
   if (!text){ box.innerHTML = '<em class="muted">Nessuna risposta</em>'; return; }


  // headings ###, ##, #
   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,   '<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>')
     .replace(/^\s*-\s+(.*)$/gm, '<li>$1</li>');
     .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:6px 0;">${line}</p>`;
   }).join('');
   }).join('');
   box.innerHTML = html;
   box.innerHTML = html;
}
}


/* === Costruzione contesto per GPT dai campi pagina === */
/* Costruzione prompt da parametri progetto */
function buildProjectContext() {
function buildProjectContext(){
  const title = (document.getElementById('newProjectTitle')?.value || '').trim();
   const goal        = (document.getElementById('p_goal')?.value||'').trim();
  const notes = (document.getElementById('newProjectNotes')?.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 { title, notes, goal, audience, deliverable, constraints };
}
}
 
function buildPromptForGPT(){
function buildPromptForGPT() {
   const c = buildProjectContext();
   const c = buildProjectContext();
   const out = [];
   const out = [];
   out.push(`You are a senior editor and project designer. Provide a clear, actionable plan.`);
   out.push('Rispondi in ITALIANO. Sei un architetto software MediaWiki+PHP.');
   if (c.title)      out.push(`\n# Title\n${c.title}`);
   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.notes)       out.push(`\n# Notes\n${c.notes}`);
   if (c.goal) out.push('\n# Obiettivo\n'+c.goal);
   if (c.goal)       out.push(`\n# Goal\n${c.goal}`);
   if (c.audience) out.push('\n# Pubblico\n'+c.audience);
   if (c.audience)   out.push(`\n# Audience\n${c.audience}`);
   if (c.deliverable) out.push('\n# Deliverable\n'+c.deliverable);
   if (c.deliverable) out.push(`\n# Deliverables\n${c.deliverable}`);
   if (c.constraints) out.push('\n# Vincoli\n'+c.constraints);
   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');
   return out.join('\n');
}
}


/* === Azione: Analizza con GPT (usa la tua API già protetta) ===
/* Analisi con GPT via proxy (usa endpoint già esistente) */
* Se NON hai ancora un endpoint dedicato, per test immediato simuliamo la risposta.
async function runProjectAnalysis(){
* 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;
   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'; }
}


    // === FAKE RESPONSE PER TEST VISIVO (subito) ===
/* Crea progetto (usa API server già presenti) */
    const fake = `# Executive Summary
async function createProject(){
Progetto focalizzato su ${ (document.getElementById('newProjectTitle')?.value || 'il tema indicato') }.
  const input = document.getElementById('newProjectName');
Obiettivo: ${ document.getElementById('p_goal')?.value || 'non specificato' }.
  const name = (input.value||'').trim();
Pubblico: ${ document.getElementById('p_audience')?.value || 'non specificato' }.
  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); }
}


## Strengths & Risks
/* Lista e rendering progetti */
- Strength: contenuti specialistici.
async function loadProjects(){
- Risk: mancano vincoli chiari (${ document.getElementById('p_constraints')?.value || '' }).
  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>'; }
}


## Step-by-step Plan (milestones)
/* 📁 COPIA DAL SERVER → PROGETTO (SANIFICATO) */
- M1: Definizione indice capitoli.
async function copyFromServerSanitized(){
- M2: Sviluppo contenuti.
  const src      = (document.getElementById('sourcePathInput')?.value||'').trim();
- M3: Revisione clinica e bibliografia.
  const project  = (document.getElementById('gpt-load-project')?.value||'').trim();
- M4: Pubblicazione.
  const subfolder = (document.getElementById('gpt-load-subfolder')?.value||'php').trim() || 'php';
  const filename  = (document.getElementById('gpt-load-filename')?.value||'').trim();


## Resources & Budget hints
  if (!src)      return alert('Inserisci il percorso assoluto del file sorgente (SERVER).');
- Autori, revisori, tool AI, hosting video.
  if (!project)  return alert('Inserisci il nome del progetto.');
  if (!filename) return alert('Inserisci il Filename di destinazione.');


## Metrics of success
  const destination = `${project}/${subfolder}/${filename}`;
- Capitoli completati, feedback medici, engagement studenti.`;


     renderGpt(fake);
  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); }
}


    // === VERSIONE REALE (quando pronta):
/* Lettura file salvato (verifica) */
    // const r = await fetch('/dashboard/api/ai_project_analyze.php', {
async function caricaFileGPT(){
    //  method: 'POST',
  const project  = (document.getElementById('gpt-load-project')?.value||'').trim();
    //   headers: {'Content-Type':'application/x-www-form-urlencoded'},
   const subfolder = (document.getElementById('gpt-load-subfolder')?.value||'php').trim()||'php';
    //  body: new URLSearchParams({ prompt })
  const filename  = (document.getElementById('gpt-load-filename')?.value||'').trim();
    // });
  if (!project || !filename) return alert('Compila Project e Filename');
    // const j = await r.json();
    // if (!j.ok) throw new Error(j.error || 'AI error');
    // renderGpt(j.text);


   } catch (e) {
   try{
     renderGpt('❌ Errore: ' + e.message);
    const url = `/dashboard/api/read_file.php?projectName=${encodeURIComponent(project)}&subfolder=${encodeURIComponent(subfolder)}&filename=${encodeURIComponent(filename)}`;
   } finally {
    const r = await fetch(url, {credentials:'include',cache:'no-store'});
    btn.disabled = false; btn.textContent = 'Analizza con GPT';
     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 del bottone */
/* Bind iniziali */
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', ()=>{
   const b = document.getElementById('btnAnalyze');
   document.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis);
   if (b) b.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

🔧 Dashboard Operativa – Masticationpedia

Centro di comando per progetti, API, file e backup

🧾 Apri Log Dashboard

📁 Importa file dal server (semplificata)

Compila i campi in basso (Project/Subfolder/Filename) per decidere la destinazione nel progetto.

📁 Carica/Leggi File GPT