Nessun oggetto della modifica
Etichetta: Annullato
Nessun oggetto della modifica
 
(37 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
<!doctype html>
<html>
<html lang="it">
   <h2>🔧 Dashboard Operativa – Masticationpedia</h2>
<head>
   <p><em>Centro di comando per progetti, API, file e backup</em></p>
   <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>
  <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a>
<p class="muted"><em>Centro di comando per progetti, API, file e backup</em></p>
  <button onclick="clearServerLog()">🧹 Svuota Log</button>


<a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a>
  <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;">
<button onclick="clearServerLog()">🧹 Svuota Log</button>
    <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button>


<!-- 🔘 Pulsanti di navigazione -->
    <button class="dashboard-toggle"
<div style="margin: 1rem 0 1.2rem 0; display:flex; flex-wrap:wrap; gap:.6rem;">
      onclick="(window.showMpAI ? showMpAI() : (function(){var el=document.getElementById('mpAI'); if(el){ el.style.display='block'; el.scrollIntoView({behavior:'smooth'});} else { alert('#mpAI non trovato'); } })())">
  <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button>
      🤖 Masticationpedia AI
  <button class="dashboard-toggle" onclick="toggleDashboardBox('project-status')">📊 Stato Progetti</button>
    </button>
  <button class="dashboard-toggle" onclick="toggleDashboardBox('chatgpt-plus')">🤖 ChatGPT plus</button>
  </div>
  <button class="dashboard-toggle" onclick="toggleDashboardBox('test-tools')">🧪 Strumenti di Test</button>
  <button class="dashboard-toggle" onclick="toggleDashboardBox('activity-log')">📘 Registro Attività</button>
  <button class="dashboard-toggle" onclick="uploadToOpenAI()">📄 Carica in OpenAI</button>
</div>
 
<!-- 📦 Connessione API (via server, nessuna key lato client) -->
<div id="api-settings" class="dashboard-box" style="display:none;">
  <strong>Connessione API (protetta dal server)</strong><br><br>


   <label>Modello</label>
   <!-- ============== MASTICATIONPEDIA AI ============== -->
  <select id="model-select" style="width:100%; margin-bottom:0.5rem;">
  <div id="mpAI" class="mpai-root" style="display:none">
    <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
     <div class="mpai-header">
    <option value="gpt-4o-mini">gpt-4o-mini</option>
       <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div>
    <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
       <div class="mpai-actions">
  </select>
         <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button>
 
         <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button>
  <label>Prompt di test</label>
         <button id="mpai-save-as-project" class="mpai-btn">💾 Salva come progetto</button>
  <textarea id="test-prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea><br>
 
  <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>
</div>
 
<!-- 📊 Stato Progetti -->
<div id="project-status" class="dashboard-box" style="display:none; margin-top:1rem;">
  <strong>📊 Stato Progetti</strong><br><br>
 
  <!-- 🧠 Risposta GPT – Analisi progetto -->
  <div>
    <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label>
     <div id="gptResponse" class="gpt-card">
       <em class="muted">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em>
    </div>
 
    <!-- Parametri per dare contesto a GPT -->
    <div id="gptParams" class="dashboard-box" style="margin-top:12px; border-style:dashed;">
       <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
         <input id="p_goal"       placeholder="🎯 Obiettivo (es. Attivare SSO LinkedIn)" />
        <input id="p_audience"    placeholder="👥 Pubblico (es. amministratori, dev)" />
         <input id="p_deliverable" placeholder="📦 Output atteso (es. patch LocalSettings + file PHP)" />
        <input id="p_constraints" placeholder="⏱️ Vincoli (es. MediaWiki 1.43, niente conflitti)" />
      </div>
      <div style="margin-top:10px;">
         <button id="btnAnalyze" class="primary">Analizza con GPT</button>
       </div>
       </div>
     </div>
     </div>
  </div>


  <!-- 📂 Gestione Progetti -->
    <div class="mpai-grid">
  <div class="projects-controls dashboard-box" style="margin-top:1rem; background:#fff;">
      <aside class="mpai-col mpai-left">
    <h4>📂 Gestione Progetti</h4>
        <h4>📂 Progetti</h4>
    <div style="display:flex;gap:6px;flex-wrap:wrap;">
        <div id="mpai-project-list" class="mpai-projects">
      <input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)">
          <em class="muted">Carico progetti…</em>
      <button id="btnCreateProject" class="primary">Crea progetto</button>
        </div>
    </div>
        <div class="mpai-new-project">
    <ul id="projectsList" class="projects-list" style="margin-top:1rem; list-style:none; padding:0;"></ul>
          <input id="mpai-project-name" type="text" placeholder="Nuovo progetto (A-Z 0-9 _ -)" />
  </div>
          <button id="mpai-project-create" class="mpai-btn">Crea</button>
</div>
        </div>
        <div class="mpai-help muted">
          Ogni progetto mantiene la sua cronologia locale (browser).
        </div>
      </aside>


<!-- 🤖 ChatGPT plus (stato attuale) -->
      <main class="mpai-col mpai-center">
<div id="chatgpt-plus" class="dashboard-box" style="display:none; margin-top:1rem;">
        <div id="mpai-uploads" class="mpai-uploads" data-state="idle">
  <strong>ChatGPT plus – Generazione capitoli</strong><br><br>
          <div class="mpai-dropzone" id="mpai-dropzone">
            <div>
              <div class="muted" style="margin-bottom:6px;">Trascina qui file <b>sanificati</b> oppure</div>
              <label class="mpai-file-label">
                <input id="mpai-file-input" type="file" multiple style="display:none;">
                <span class="mpai-btn">📂 Aggiungi file</span>
              </label>
            </div>
          </div>
          <div id="mpai-file-list" class="mpai-filelist"></div>
        </div>


  <!-- Barra superiore -->
        <div id="mpai-chat" class="mpai-chat">
  <div style="display:flex; gap:12px; flex-wrap:wrap; margin-bottom:10px;">
          <div class="mpai-msg mpai-hint">
    <label>📁 Progetto:
            Benvenuto. Seleziona un progetto a sinistra, oppure scrivi subito: se non c’è un progetto creo una conversazione locale. Le risposte appariranno qui sotto in ordine, come in ChatGPT.
      <select id="mpChatProject">
          </div>
        <option value="Generazione_capitoli" selected>Generazione capitoli</option>
         </div>
      </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 class="mpai-composer">
  <div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:8px;">
          <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea>
    <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 -->
          <div class="mpai-composer-right">
  <label><b>Domanda</b> (breve, come in ChatGPT):</label>
            <label class="mpai-check" style="margin:0 0 6px 0;">
  <div style="display:flex; gap:8px; align-items:flex-start; margin-bottom:8px;">
              <input id="mpai-drop-urls" type="checkbox">
    <textarea id="mpChatPrompt" rows="3" style="flex:1 1 auto; width:100%;"></textarea>
              Ignora/Cancella URL e allegati per questo invio
    <button id="mpChatSend">Invia</button>
            </label>
  </div>
            <button id="mpai-send" class="mpai-btn primary">Invia</button>
          </div>
        </div>
      </main>


  <!-- Output risposta -->
      <aside class="mpai-col mpai-right">
  <label><b>Risposta</b> (box grande):</label>
        <h4>⚙️ Impostazioni</h4>
  <div id="mpChatAnswer" style="min-height:220px; border:1px solid #ddd; background:#fff; border-radius:8px; padding:10px; white-space:pre-wrap;"></div>
        <label class="mpai-field">Modello
 
          <select id="mpai-model">
  <!-- Explorer semplice dei file salvati -->
            <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
  <div style="margin-top:10px; border-top:1px dashed #ddd; padding-top:10px;">
            <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
    <div style="display:flex; align-items:center; gap:8px; margin-bottom:6px;">
          </select>
      <strong>🗂️ File salvati (output/)</strong>
        </label>
      <button id="mpChatRefresh"
        <label class="mpai-field">Temperatura
        title="Aggiorna elenco"
          <input id="mpai-temp" type="number" min="0" max="1" step="0.1" value="0.7">
        onclick="window.__forceRefreshList && window.__forceRefreshList()">Aggiorna</button>
        </label>
    </div>
        <label class="mpai-check">
    <div id="mpSavedList" class="list">
          <input id="mpai-sanitized-only" type="checkbox" checked>
      <em style="color:#777;">Nessun file elencato. Clicca “Aggiorna”.</em>
          Usa solo file <b>sanificati</b> come contesto
        </label>
        <div class="mpai-help muted">
          Le API key restano lato server (file sicuro). Il browser non vede mai le chiavi.
        </div>
      </aside>
     </div>
     </div>
   </div>
   </div>


   <!-- Azioni -->
   <!-- ============ STILI (CSS) ============ -->
   <div style="margin-top:8px; display:flex; gap:8px; justify-content:flex-end;">
   <style>
     <button id="mpChatClear">Pulisci</button>
    .mpai-root{--bg:#f7f8fa;--card:#fff;--muted:#6b7280;--line:#e5e7eb;--text:#111827;--primary:#0a7cff;--danger:#e74c3c;--accent:#eef2ff;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:var(--text)}
     <button id="mpChatCopy">Copia risposta</button>
    .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px}
     <button id="mpChatSave">📥 Salva risposta</button>
    .mpai-title{font-size:20px;font-weight:700}
   </div>
    .mpai-actions{display:flex;gap:8px}
</div>
    .mpai-btn{border:1px solid var(--line);background:#fff;padding:.45rem .8rem;border-radius:8px;cursor:pointer}
    .mpai-btn:hover{background:#f3f4f6}
    .mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff}
    .mpai-btn.danger{background:var(--danger);border-color:var(--danger);color:#fff}
    .mpai-btn.ghost{background:transparent}
    .muted{color:var(--muted)}
    .mpai-grid{display:grid;grid-template-columns:260px minmax(0,1fr) 260px;gap:12px}
    .mpai-col{background:#fff;border:1px solid var(--line);border-radius:12px;padding:12px}
    .mpai-left,.mpai-right{max-height:75vh;overflow:auto}
    .mpai-projects{display:flex;flex-direction:column;gap:6px;margin:8px 0}
    .mpai-projects .row{display:flex;align-items:center;gap:6px;justify-content:space-between;border:1px solid var(--line);border-radius:8px;padding:6px 8px}
    .mpai-projects .row .title{font-family:monospace;font-size:12px}
    .mpai-new-project{display:flex;gap:6px;margin-top:8px}
    .mpai-new-project input{flex:1 1 auto;padding:.45rem .6rem;border:1px solid var(--line);border-radius:8px}
    .mpai-field{display:flex;flex-direction:column;gap:4px;margin:8px 0}
    .mpai-field input,.mpai-field select{padding:.45rem .6rem;border:1px solid var(--line);border-radius:8px}
    .mpai-check{display:flex;align-items:center;gap:8px;margin:8px 0}
    .mpai-center{display:flex;flex-direction:column;gap:10px}
    .mpai-uploads{border:1px dashed var(--line);border-radius:12px;padding:10px;background:#fafafa}
    .mpai-dropzone{display:flex;align-items:center;justify-content:center;border-radius:10px;padding:12px;background:var(--accent);text-align:center}
    .mpai-filelist{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
    .mpai-filepill{display:flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:999px;padding:4px 8px;background:#fff;font-size:12px}
    .mpai-filepill button{border:none;background:transparent;cursor:pointer;color:#c00}
    .mpai-chat{display:flex;flex-direction:column;gap:10px;max-height:50vh;overflow:auto;border:1px solid var(--line);border-radius:12px;padding:10px;background:#fff}
    .mpai-msg{border:1px solid var(--line);border-radius:12px;padding:10px;background:#fff}
    .mpai-msg.user{border-color:#bee3f8;background:#eff6ff}
    .mpai-msg.assistant{border-color:#d1fae5;background:#f0fdf4}
    .mpai-msg.error{border-color:#fecaca;background:#fff1f2}
    .mpai-msg .role{font-weight:600;margin-bottom:4px}
    .mpai-hint{border-style:dashed;color:var(--muted)}
    .mpai-composer{display:flex;gap:8px;align-items:flex-end}
    .mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px}
    .mpai-composer-right{display:flex;flex-direction:column;gap:6px}
 
     /* --- Blocchi codice nelle risposte + copia --- */
    .mp-code{border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;margin:.5rem 0;background:#fff}
    .mp-code__hdr{display:flex;justify-content:space-between;align-items:center;padding:.5rem .75rem;border-bottom:1px solid #e5e7eb;background:#f8fafc}
    .mp-code__lang{font-family:ui-monospace, Menlo, Consolas, monospace;color:#334155}
    .mp-copy{border:1px solid #e5e7eb;background:#fff;border-radius:8px;padding:.25rem .5rem;cursor:pointer}
    .mp-copy:hover{background:#f1f5f9}
     .mp-code pre{margin:0;padding:.75rem 1rem;overflow:auto;font-family:ui-monospace, Menlo, Consolas, monospace;font-size:13px}
     @media (max-width:1100px){.mpai-grid{grid-template-columns:1fr}.mpai-left,.mpai-right{max-height:none}}
   </style>


<!-- 📁 Importa file dal server (semplificata) -->
  <!-- ========= SEZIONI LEGACY (nascoste) ========= -->
<div id="serverFileSimpleImportContainer" class="dashboard-box" style="margin-top:1rem;">
  <div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;">
  <h4>📁 Importa file dal server (semplificata)</h4>
    <strong>Connessione API (protetta dal server)</strong><br><br>


  <!-- Nuovo bottone: Copia sanificato → progetto -->
    <label>Modello</label>
  <div style="margin:.4rem 0 .6rem 0;">
    <select id="model-select" style="width:100%; margin-bottom:0.5rem;">
    <button class="primary" onclick="copyFromServerSanitized()">🔐 Copia (sanificato) → progetto</button>
      <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
    <small class="muted" style="display:block;margin-top:6px;">
      <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
      Compila i campi in basso (Project/Subfolder/Filename) per decidere la destinazione nel progetto.
    </select>
    </small>
  </div>


  <div style="display:grid;grid-template-columns:1fr;gap:6px;max-width:820px;">
     <label>Prompt di test</label>
     <label for="sourcePathInput"><b>📂 Percorso Assoluto del file sorgente (SERVER):</b></label>
     <textarea id="test_prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea>
     <input type="text" id="sourcePathInput" placeholder="/var/www/html/masticationpedia-staging/LocalSettings.php" />
  </div>
</div>


<!-- 📁 Caricamento/lettura File GPT (destinazione) -->
    <label style="display:inline-flex; gap:.4rem; align-items:center; margin:.25rem 0 .75rem;">
<div class="dashboard-box" style="margin-top:1rem;">
      <input id="test_drop_urls" type="checkbox">
  <h3>📁 Carica/Leggi File GPT</h3>
      Ignora/Cancella URL e allegati per questo invio
  <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
     </label><br>
    <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>


<!-- 🧪 Console JS -->
    <button id="test_run">Esegui</button>
<div id="test-tools" class="dashboard-box" style="display:none; margin-top:1rem;">
  <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>


<!-- 📘 Registro Attività -->
    <pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre>
<div id="activity-log" class="dashboard-box" style="display:none; margin-top:1rem;">
  <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>
  <div id="activityLogContent" style="margin-top:1rem; max-height:250px; overflow-y:auto; background:#f0f0f0; padding:1rem; border:1px solid #ccc; border-radius:6px; font-family:monospace; font-size:0.85rem;">
    <em><span style="color:#888;">Registro avviato...</span></em>
   </div>
   </div>
</div>
<!-- ✅ Carica JS esterno (se lo usi) -->
<script src="/dashboard/api/dashboard.js?v=20250907d"></script>
<!-- ============ JS inline ============ -->
<script>
<script>
/* UI base */
// JS minimo temporaneo per sbloccare la pagina
function toggleDashboardBox(id){
// (serve solo a NON avere errori di sintassi)
  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(){
  const prompt = (document.getElementById('test-prompt')?.value||'').trim();
  const output = document.getElementById('api-result');
  const model  = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13';
  if (!prompt){ output.innerText='⚠️ Inserisci un prompt di test.'; return; }
  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 data = await res.json();
    if (data.status==='ok' && data.result){
      output.innerText = '✅ Risposta: '+data.result;
    } else {
      output.innerText = '❌ Errore: ' + (data.error || 'Risposta vuota');
    }
  }catch(e){ output.innerText='🚫 Errore di rete: '+e.message; }
}
 
/* Renderer stile ChatGPT per la risposta GPT */
function renderGpt(text){
  const box = document.getElementById('gptResponse');
  if (!box) return;
  if (!text){ box.innerHTML = '<em class="muted">Nessuna risposta</em>'; return; }
 
  let html = (text||'').replace(/\r\n/g,'\n')
    .replace(/^\s*#{3}\s?(.*)$/gm,'<h3 style="font-size:18px;margin:14px 0 6px;">$1</h3>')
    .replace(/^\s*#{2}\s?(.*)$/gm,'<h2 style="font-size:20px;margin:16px 0 8px;">$1</h2>')
    .replace(/^\s*#\s?(.*)$/gm,  '<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>')
    .replace(/^\s*-\s+(.*)$/gm,  '<li>$1</li>');
  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}</p>`;
  }).join('');
  box.innerHTML = html;
}
 
/* Costruzione prompt da parametri progetto */
function buildProjectContext(){
  const goal        = (document.getElementById('p_goal')?.value||'').trim();
  const audience    = (document.getElementById('p_audience')?.value||'').trim();
  const deliverable = (document.getElementById('p_deliverable')?.value||'').trim();
  const constraints = (document.getElementById('p_constraints')?.value||'').trim();
  return {goal,audience,deliverable,constraints};
}
function buildPromptForGPT(){
  const c = buildProjectContext();
  const out = [];
  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.audience) out.push('\n# Pubblico\n'+c.audience);
  if (c.deliverable) out.push('\n# Deliverable\n'+c.deliverable);
  if (c.constraints) out.push('\n# Vincoli\n'+c.constraints);
  return out.join('\n');
}
 
/* Analisi con GPT via proxy (usa endpoint già esistente) */
async function runProjectAnalysis(){
  const btn = document.getElementById('btnAnalyze');
  if (!btn) return;
  btn.disabled = true; btn.textContent='Analizzo…';
  try{
    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{
    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.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis);
  document.getElementById('btnCreateProject')?.addEventListener('click', createProject);
  loadProjects();
});
</script>
</script>


</body>
</html>
</html>

Versione attuale delle 15:48, 19 ott 2025

🔧 Dashboard Operativa – Masticationpedia

Centro di comando per progetti, API, file e backup

🧾 Apri Log Dashboard