|
|
| (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> |