Nessun oggetto della modifica
Etichetta: Ripristino manuale
Nessun oggetto della modifica
Riga 5: Riga 5:
<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 -->
Riga 18: Riga 16:
</div>
</div>


<!-- 📦 Connessione API (via server, nessuna key lato client) -->
<!-- 📦 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>
Riga 50: Riga 48:
     </div>
     </div>


     <!-- Parametri per dare contesto a GPT -->
     <!-- Parametri -->
     <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 71: Riga 69:
     <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;">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>
</div>
<!-- 🤖 ChatGPT plus (unico contenitore) -->
<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>
  <!-- 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>
  <!-- Output risposta -->
  <label><b>Risposta</b> (box grande):</label>
  <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 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>
  <!-- Azioni -->
  <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>
<!-- 📁 Caricamento File GPT -->
<div style="margin-top:2rem;">
  <h3>📁 Carica File GPT</h3>
  <label>📁 Project: <input type="text" id="gpt-load-project" value="SSO_LinkedIn" /></label>
  <label>📂 Subfolder: <input type="text" id="gpt-load-subfolder" value="php" /></label>
  <label>📄 Filename: <input type="text" id="gpt-load-filename" placeholder="es: testGPT.php" /></label>
  <button onclick="caricaFileGPT()">📁 Carica file</button>
</div>
<!-- 🧪 Console JS -->
<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>


Riga 176: Riga 82:
</div>
</div>


<!-- ✅ Carica JS esterno (se lo usi) -->
<!-- ✅ JS -->
<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>
// ✅ 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…';
 
  try {
    const r = await fetch('/dashboard/api/ai_check.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
      body: new URLSearchParams({ language, code })
    });
    const j = await r.json();
 
    if (j.ok) {
      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 {
      outEl.textContent = '❌ Errore: ' + (j.error || 'sconosciuto');
      if (j.raw) outEl.textContent += '\n\nRAW:\n' + j.raw;
    }
  } catch (e) {
    outEl.textContent = '❌ Errore rete: ' + e.message;
  }
};
</script>
 
<script>
<script>
/* === Renderer stile ChatGPT: trasforma testo in HTML arioso === */
/* === Renderer stile ChatGPT === */
function renderGpt(text) {
function renderGpt(text) {
   const box = document.getElementById('gptResponse');
   const box = document.getElementById('gptResponse');
Riga 291: Riga 90:
   if (!text) { box.innerHTML = '<em style="opacity:.7">Nessuna risposta</em>'; return; }
   if (!text) { box.innerHTML = '<em style="opacity:.7">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(/^### (.*)$/gm, '<h3 style="margin:14px 0 6px;font-size:17px;">$1</h3>')
     .replace(/^\s*#{2}\s?(.*)$/gm, '<h2 style="font-size:20px;margin:16px 0 8px;">$1</h2>')
     .replace(/^## (.*)$/gm, '<h2 style="margin:16px 0 8px;font-size:19px;">$1</h2>')
     .replace(/^\s*#\s?(.*)$/gm,  '<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>')
     .replace(/^# (.*)$/gm,  '<h1 style="margin:18px 0 10px;font-size:21px;">$1</h1>')
     .replace(/^\s*-\s+(.*)$/gm,   '<li>$1</li>');
     .replace(/^\- (.*)$/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>`);


  // paragrafi (evita di wrappare headings/list)
   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:8px 0;">${line}</p>`;
     return `<p style="margin:6px 0;line-height:1.5">${line}</p>`;
   }).join('');
   }).join('');


Riga 311: Riga 107:
}
}


/* === Costruzione contesto per GPT dai campi pagina === */
/* === Costruzione prompt === */
function buildProjectContext() {
function buildProjectContext() {
   const title = (document.getElementById('newProjectTitle')?.value || '').trim();
   return {
  const notes = (document.getElementById('newProjectNotes')?.value || '').trim();
    title: (document.getElementById('newProjectTitle')?.value || '').trim(),
  const goal       = (document.getElementById('p_goal')?.value || '').trim();
    notes: (document.getElementById('newProjectNotes')?.value || '').trim(),
  const audience   = (document.getElementById('p_audience')?.value || '').trim();
    goal: (document.getElementById('p_goal')?.value || '').trim(),
  const deliverable = (document.getElementById('p_deliverable')?.value || '').trim();
    audience: (document.getElementById('p_audience')?.value || '').trim(),
  const constraints = (document.getElementById('p_constraints')?.value || '').trim();
    deliverable: (document.getElementById('p_deliverable')?.value || '').trim(),
   return { title, notes, goal, audience, deliverable, constraints };
    constraints: (document.getElementById('p_constraints')?.value || '').trim()
   };
}
}


Riga 325: Riga 122:
   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(`Tu sei un project manager ed editor senior. Rispondi sempre in **italiano**. Fornisci un piano chiaro, sintetico e leggibile.`);
   if (c.title)      out.push(`\n# Title\n${c.title}`);
   if (c.title)      out.push(`\n# Titolo\n${c.title}`);
   if (c.notes)      out.push(`\n# Notes\n${c.notes}`);
   if (c.notes)      out.push(`\n# Note\n${c.notes}`);
   if (c.goal)        out.push(`\n# Goal\n${c.goal}`);
   if (c.goal)        out.push(`\n# Obiettivo\n${c.goal}`);
   if (c.audience)    out.push(`\n# Audience\n${c.audience}`);
   if (c.audience)    out.push(`\n# Pubblico\n${c.audience}`);
   if (c.deliverable) out.push(`\n# Deliverables\n${c.deliverable}`);
   if (c.deliverable) out.push(`\n# Output atteso\n${c.deliverable}`);
   if (c.constraints) out.push(`\n# Constraints\n${c.constraints}`);
   if (c.constraints) out.push(`\n# Vincoli\n${c.constraints}`);
   out.push(`\n# Output format
   out.push(`\n# Formato output richiesto
- Executive Summary
- Sommario introduttivo
- Strengths & Risks
- Punti di forza e rischi
- Step-by-step Plan (milestones)
- Piano step-by-step (milestones)
- Resources & Budget hints
- Risorse e budget
- Metrics of success`);
- Metriche di successo`);
   return out.join('\n');
   return out.join('\n');
}
}


/* === Azione: Analizza con GPT (usa la tua API già protetta) ===
/* === Analisi progetto === */
* 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() {
async function runProjectAnalysis() {
   const btn = document.getElementById('btnAnalyze');
   const btn = document.getElementById('btnAnalyze');
Riga 353: Riga 147:
     const prompt = buildPromptForGPT();
     const prompt = buildPromptForGPT();


     // === FAKE RESPONSE PER TEST VISIVO (subito) ===
     // SIMULAZIONE (puoi sostituire con fetch reale)
     const fake = `# Executive Summary
     const fake = `# Sommario introduttivo
Progetto focalizzato su ${ (document.getElementById('newProjectTitle')?.value || 'il tema indicato') }.
Analisi del progetto: ${ (document.getElementById('newProjectTitle')?.value || 'titolo non specificato') }.
Obiettivo: ${ document.getElementById('p_goal')?.value || 'non specificato' }.
Obiettivo: ${ document.getElementById('p_goal')?.value || 'non specificato' }.
Pubblico: ${ document.getElementById('p_audience')?.value || 'non specificato' }.
Pubblico: ${ document.getElementById('p_audience')?.value || 'non specificato' }.


## Strengths & Risks
## Punti di forza e rischi
- Strength: contenuti specialistici.
- Punti di forza: contenuti specialistici.
- Risk: mancano vincoli chiari (${ document.getElementById('p_constraints')?.value || '—' }).
- Rischi: vincoli non chiariti (${ document.getElementById('p_constraints')?.value || '—' }).


## Step-by-step Plan (milestones)
## Piano step-by-step
- M1: Definizione indice capitoli.
1. Definizione indice capitoli
- M2: Sviluppo contenuti.
2. Sviluppo contenuti
- M3: Revisione clinica e bibliografia.
3. Revisione clinica e bibliografia
- M4: Pubblicazione.
4. Pubblicazione


## Resources & Budget hints
## Risorse e budget
- Autori, revisori, tool AI, hosting video.
- Autori, revisori, strumenti AI, hosting


## Metrics of success
## Metriche di successo
- Capitoli completati, feedback medici, engagement studenti.`;
- Capitoli completati
- Feedback medici
- Engagement degli studenti`;


     renderGpt(fake);
     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) {
   } catch (e) {
Riga 394: Riga 180:
}
}


/* bind del bottone */
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', () => {
   const b = document.getElementById('btnAnalyze');
   const b = document.getElementById('btnAnalyze');
Riga 400: Riga 185:
});
});
</script>
</script>
</html>
</html>

Versione delle 16:23, 21 set 2025

🔧 Dashboard Operativa – Masticationpedia

Centro di comando per progetti, API, file e backup

🧾 Apri Log Dashboard