|
|
| (35 versioni intermedie di uno stesso utente non sono mostrate) |
| Riga 1: |
Riga 1: |
| <html> | | <html> |
| <head> | | <h2>🔧 Dashboard Operativa – Masticationpedia</h2> |
| <meta charset="utf-8">
| | <p><em>Centro di comando per progetti, API, file e backup</em></p> |
| <title>Dashboard Operativa – Masticationpedia</title>
| |
| <style> | |
| :root{--b:#e5e7eb;--bg:#f9fafb;--card:#ffffff;--txt:#1f2937;--muted:#6b7280;--blue:#0a7cff;--green:#28a745;}
| |
| body{background:var(--bg);color:var(--txt);font:16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}
| |
| .toolbar{display:flex;gap:10px;flex-wrap:wrap;margin:16px 0}
| |
| .btn{border:1px solid var(--b);background:var(--card);border-radius:10px;padding:10px 14px;font-weight:600;cursor:pointer}
| |
| .btn.primary{background:var(--blue);color:#fff;border-color:var(--blue)}
| |
| .btn.danger{background:#e11d48;color:#fff;border-color:#e11d48}
| |
| .link{font-weight:600}
| |
| .dash-box{background:var(--card);border:1px solid var(--b);border-radius:12px;padding:16px;margin-top:14px}
| |
| .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:var(--muted)}
| |
| .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
| |
| .grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
| |
| input[type=text], input[type=number], select, textarea{width:100%;border:1px solid var(--b);border-radius:8px;padding:8px}
| |
| textarea{min-height:120px}
| |
| ul.plain{list-style:none;padding-left:0;margin:0}
| |
| .pill{border:1px solid var(--b);border-radius:999px;padding:2px 8px;font-size:12px;color:var(--muted)}
| |
| .list-item{display:flex;align-items:center;gap:8px;justify-content:flex-start;margin:6px 0}
| |
| .list-item .actions{display:flex;gap:6px}
| |
| code{background:#f3f4f6;border:1px solid #e5e7eb;border-radius:6px;padding:2px 6px}
| |
| </style> | |
| </head> | |
|
| |
|
| <body> | | <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> |
| | <button onclick="clearServerLog()">🧹 Svuota Log</button> |
|
| |
|
| <h2>🔧 Dashboard Operativa – Masticationpedia</h2> | | <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> |
| <p class="muted">Centro di comando per progetti, API, file e backup</p> | | <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button> |
|
| |
|
| <div class="row"> | | <button class="dashboard-toggle" |
| <a class="link" href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a>
| | 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="btn" onclick="clearServerLog()">🧹 Svuota Log</button>
| | 🤖 Masticationpedia AI |
| </div> | | </button> |
| | </div> |
|
| |
|
| <!-- Pulsanti principali -->
| | <!-- ============== MASTICATIONPEDIA AI ============== --> |
| <div class="toolbar">
| | <div id="mpAI" class="mpai-root" style="display:none"> |
| <button class="btn" onclick="showBox('api-settings')">⚙️ Connessione API</button>
| | <div class="mpai-header"> |
| <button class="btn" onclick="showBox('project-status')">📊 Stato Progetti</button>
| | <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div> |
| <button class="btn" onclick="showBox('test-tools')">🧪 Strumenti di Test</button>
| | <div class="mpai-actions"> |
| <button class="btn" onclick="showBox('activity-log')">📘 Registro Attività</button> | | <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button> |
| </div>
| | <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button> |
| | | <button id="mpai-save-as-project" class="mpai-btn">💾 Salva come progetto</button> |
| <!-- ========================= | | </div> |
| ⚙️ Connessione API (proxy)
| |
| ========================= -->
| |
| <div id="api-settings" class="dash-box" style="display:none"> | |
| <strong>Connessione API (protetta dal server)</strong><br><br>
| |
| <div class="grid2">
| |
| <div>
| |
| <label>Modello</label>
| |
| <select id="model-select"> | |
| <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> | |
| <option value="gpt-4o-mini">gpt-4o-mini</option> | |
| <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> | |
| </select> | |
| </div> | | </div> |
| <div>
| |
| <label>Prompt di test</label>
| |
| <input id="test-prompt" type="text" value="Dimmi una curiosità sulla mandibola">
| |
| </div>
| |
| </div>
| |
| <div class="row" style="margin-top:10px">
| |
| <button class="btn primary" onclick="testAPIConnection()">▶️ Esegui</button>
| |
| <span class="pill">via <code>/dashboard/api/openai_project_gpt.php</code></span>
| |
| </div>
| |
| <pre id="api-result" class="dash-box" style="white-space:pre-wrap;margin-top:12px"></pre>
| |
| </div>
| |
|
| |
|
| <!-- ===================== | | <div class="mpai-grid"> |
| 📊 STATO PROGETTI
| | <aside class="mpai-col mpai-left"> |
| ===================== -->
| | <h4>📂 Progetti</h4> |
| <div id="project-status" class="dash-box" style="display:block"> | | <div id="mpai-project-list" class="mpai-projects"> |
| <strong>📊 Stato Progetti</strong>
| | <em class="muted">Carico progetti…</em> |
| | </div> |
| | <div class="mpai-new-project"> |
| | <input id="mpai-project-name" type="text" placeholder="Nuovo progetto (A-Z 0-9 _ -)" /> |
| | <button id="mpai-project-create" class="mpai-btn">Crea</button> |
| | </div> |
| | <div class="mpai-help muted"> |
| | Ogni progetto mantiene la sua cronologia locale (browser). |
| | </div> |
| | </aside> |
|
| |
|
| <!-- 🧠 Risposta GPT -->
| | <main class="mpai-col mpai-center"> |
| <div style="margin-top: 12px">
| | <div id="mpai-uploads" class="mpai-uploads" data-state="idle"> |
| <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label>
| | <div class="mpai-dropzone" id="mpai-dropzone"> |
| <div id="gptResponse" class="gpt-card">
| | <div> |
| <em class="muted">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em>
| | <div class="muted" style="margin-bottom:6px;">Trascina qui file <b>sanificati</b> oppure</div> |
| </div>
| | <label class="mpai-file-label"> |
| </div>
| | <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> |
|
| |
|
| <!-- Parametri -->
| | <div id="mpai-chat" class="mpai-chat"> |
| <div class="dash-box" style="margin-top:12px;border-style:dashed">
| | <div class="mpai-msg mpai-hint"> |
| <div class="grid2">
| | 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. |
| <input id="p_goal" placeholder="🎯 Obiettivo (es. Attivare SSO MediaWiki classico)">
| | </div> |
| <input id="p_audience" placeholder="👥 Pubblico (es. amministratori, sviluppatori)">
| | </div> |
| <input id="p_deliverable" placeholder="📦 Output atteso (es. patch LocalSettings + file PHP)">
| |
| <input id="p_constraints" placeholder="⏱️ Vincoli (es. MediaWiki 1.43, evitare conflitti)">
| |
| </div>
| |
| <div class="row" style="margin-top:10px">
| |
| <button id="btnAnalyze" class="btn primary">Analizza con GPT</button>
| |
| <span class="pill">usa <code>/dashboard/api/openai_project_gpt.php</code></span>
| |
| </div>
| |
| </div>
| |
|
| |
|
| <!-- 📂 Gestione Progetti -->
| | <div class="mpai-composer"> |
| <div class="dash-box" style="margin-top:12px;background:#fff">
| | <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> |
| <h4>📂 Gestione Progetti</h4>
| |
| <div class="row">
| |
| <input id="newProjectName" type="text" placeholder="Nome progetto (A-Z 0-9 _ -)" style="max-width:300px">
| |
| <button id="btnCreateProject" class="btn primary">Crea progetto</button>
| |
| </div>
| |
|
| |
|
| <ul id="projectsList" class="plain" style="margin-top:10px">
| | <div class="mpai-composer-right"> |
| <!-- popolato via JS -->
| | <label class="mpai-check" style="margin:0 0 6px 0;"> |
| </ul>
| | <input id="mpai-drop-urls" type="checkbox"> |
| </div>
| | Ignora/Cancella URL e allegati per questo invio |
| </div> | | </label> |
| | <button id="mpai-send" class="mpai-btn primary">Invia</button> |
| | </div> |
| | </div> |
| | </main> |
|
| |
|
| <!-- ===================== | | <aside class="mpai-col mpai-right"> |
| 🧪 STRUMENTI DI TEST
| | <h4>⚙️ Impostazioni</h4> |
| ===================== -->
| | <label class="mpai-field">Modello |
| <div id="test-tools" class="dash-box" style="display:none"> | | <select id="mpai-model"> |
| <strong>Console JS</strong>
| | <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> |
| <textarea id="codeArea" placeholder="Scrivi codice JS da testare…"></textarea>
| | <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> |
| <div class="row" style="margin-top:10px">
| | </select> |
| <button class="btn" onclick="runTestCode()">▶️ Esegui</button>
| | </label> |
| <button class="btn" onclick="checkCodeAI()">🔍 Controlla con AI</button>
| | <label class="mpai-field">Temperatura |
| | <input id="mpai-temp" type="number" min="0" max="1" step="0.1" value="0.7"> |
| | </label> |
| | <label class="mpai-check"> |
| | <input id="mpai-sanitized-only" type="checkbox" checked> |
| | 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> |
| <pre id="consoleOutput" class="dash-box" style="background:#111;color:#0f0;white-space:pre-wrap"></pre>
| |
| </div>
| |
|
| |
|
| <!-- ===================== | | <!-- ============ STILI (CSS) ============ --> |
| 📘 REGISTRO ATTIVITÀ
| | <style> |
| ===================== -->
| | .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)} |
| <div id="activity-log" class="dash-box" style="display:none"> | | .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px} |
| <div class="row" style="justify-content:space-between">
| | .mpai-title{font-size:20px;font-weight:700} |
| <strong>📘 Registro attività</strong> | | .mpai-actions{display:flex;gap:8px} |
| <button class="btn danger" onclick="clearActivityLog()">🧹 Svuota</button> | | .mpai-btn{border:1px solid var(--line);background:#fff;padding:.45rem .8rem;border-radius:8px;cursor:pointer} |
| </div>
| | .mpai-btn:hover{background:#f3f4f6} |
| <div id="activityLogContent" class="dash-box" style="margin-top:10px;max-height:260px;overflow:auto;font-family:monospace;font-size:13px">
| | .mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff} |
| <em class="muted">Registro avviato…</em> | | .mpai-btn.danger{background:var(--danger);border-color:var(--danger);color:#fff} |
| </div>
| | .mpai-btn.ghost{background:transparent} |
| </div>
| | .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 --- */ |
| JS
| | .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} |
| <script>
| | .mp-code__lang{font-family:ui-monospace, Menlo, Consolas, monospace;color:#334155} |
| /* ---------- util UI ---------- */ | | .mp-copy{border:1px solid #e5e7eb;background:#fff;border-radius:8px;padding:.25rem .5rem;cursor:pointer} |
| function showBox(id){
| | .mp-copy:hover{background:#f1f5f9} |
| ['api-settings','project-status','test-tools','activity-log'].forEach(x=>{
| | .mp-code pre{margin:0;padding:.75rem 1rem;overflow:auto;font-family:ui-monospace, Menlo, Consolas, monospace;font-size:13px} |
| const el = document.getElementById(x); | | @media (max-width:1100px){.mpai-grid{grid-template-columns:1fr}.mpai-left,.mpai-right{max-height:none}} |
| if (el) el.style.display = (x===id?'block':'none');
| | </style> |
| }); | |
| }
| |
|
| |
|
| /* ---------- log ---------- */
| | <!-- ========= SEZIONI LEGACY (nascoste) ========= --> |
| function logActivity(msg){
| | <div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> |
| const box = document.getElementById('activityLogContent');
| | <strong>Connessione API (protetta dal server)</strong><br><br> |
| if(!box) return;
| |
| const t = new Date().toLocaleTimeString('it-IT');
| |
| const line = document.createElement('div'); | |
| line.innerHTML = '<span class="muted">['+t+']</span> ' + msg;
| |
| box.prepend(line);
| |
| }
| |
| function clearActivityLog(){
| |
| const box = document.getElementById('activityLogContent');
| |
| if (box) { box.innerHTML = '<em class="muted">Registro svuotato.</em>'; logActivity('🧹 Log svuotato.'); }
| |
| }
| |
|
| |
|
| /* ---------- svuota log server ---------- */ | | <label>Modello</label> |
| async function clearServerLog(){
| | <select id="model-select" style="width:100%; margin-bottom:0.5rem;"> |
| try{
| | <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> |
| const r = await fetch('/dashboard/api/log_clear.php', {credentials:'include'}); | | <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> |
| const j = await r.json();
| | </select> |
| if (j.ok){
| |
| alert('Log svuotato.\n' + (j.cleared||[]).join('\n'));
| |
| logActivity('🧹 Log server svuotato');
| |
| window.open('/dashboard/api/log_view.php?n=300','_blank');
| |
| }else{
| |
| alert('Errore svuota log: ' + (j.error||''));
| |
| }
| |
| }catch(e){ alert('Errore rete: '+e.message); }
| |
| }
| |
|
| |
|
| /* ---------- renderer GPT ---------- */ | | <label>Prompt di test</label> |
| function renderGpt(text){
| | <textarea id="test_prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea> |
| 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')
| | <label style="display:inline-flex; gap:.4rem; align-items:center; margin:.25rem 0 .75rem;"> |
| .replace(/^\s*#{3}\s?(.*)$/gm,'<h3 style="font-size:18px;margin:14px 0 6px;">$1</h3>') | | <input id="test_drop_urls" type="checkbox"> |
| .replace(/^\s*#{2}\s?(.*)$/gm,'<h2 style="font-size:20px;margin:16px 0 8px;">$1</h2>')
| | Ignora/Cancella URL e allegati per questo invio |
| .replace(/^\s*#\s?(.*)$/gm,'<h1 style="font-size:22px;margin:18px 0 10px;">$1</h1>')
| | </label><br> |
| .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:8px 0;">${line}</p>`;
| |
| }).join('');
| |
| box.innerHTML = html;
| |
| }
| |
|
| |
|
| /* ---------- prompt per GPT ---------- */ | | <button id="test_run">Esegui</button> |
| function buildPromptForGPT(){
| |
| const g = (document.getElementById('p_goal')?.value || '').trim();
| |
| const a = (document.getElementById('p_audience')?.value || '').trim();
| |
| const d = (document.getElementById('p_deliverable')?.value || '').trim();
| |
| const c = (document.getElementById('p_constraints')?.value || '').trim();
| |
|
| |
|
| const out = [];
| | <pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> |
| out.push('Sei un project designer senior. Rispondi in italiano con piano chiaro, operativo.');
| | </div> |
| if (g) out.push('\\n# Obiettivo\\n'+g);
| | <script> |
| if (a) out.push('\\n# Pubblico\\n'+a);
| | // JS minimo temporaneo per sbloccare la pagina |
| if (d) out.push('\\n# Output atteso\\n'+d);
| | // (serve solo a NON avere errori di sintassi) |
| if (c) out.push('\\n# Vincoli\\n'+c);
| |
| out.push('\\n# Output richiesto\\n- Executive Summary\\n- Punti di forza & rischi\\n- Piano step-by-step (milestones)\\n- Risorse & budget\\n- Metriche di successo');
| |
| return out.join('\\n');
| |
| }
| |
| | |
| /* ---------- analisi con GPT (via proxy server) ---------- */
| |
| async function runProjectAnalysis(){
| |
| const btn = document.getElementById('btnAnalyze');
| |
| btn.disabled = true; btn.textContent = 'Analizzo…';
| |
| try{
| |
| const model = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13';
| |
| const prompt = buildPromptForGPT(); | |
| logActivity('🤖 Analisi GPT via proxy – modello: <b>'+model+'</b>');
| |
| | |
| const r = await fetch('/dashboard/api/openai_project_gpt.php', {
| |
| method:'POST',
| |
| headers:{'Content-Type':'application/json'},
| |
| credentials:'include',
| |
| body: JSON.stringify({ model, prompt })
| |
| });
| |
| const j = await r.json();
| |
| if (j.status==='ok' && j.result){
| |
| renderGpt(j.result);
| |
| logActivity('✅ Analisi GPT completata');
| |
| }else{
| |
| renderGpt('❌ Errore: ' + (j.error||'risposta vuota'));
| |
| logActivity('❌ Errore GPT: ' + (j.error||'vuota'));
| |
| }
| |
| }catch(e){
| |
| renderGpt('❌ Errore rete: '+e.message);
| |
| }finally{
| |
| btn.disabled=false; btn.textContent='Analizza con GPT';
| |
| }
| |
| }
| |
| | |
| /* ---------- API progetto ---------- */
| |
| const API = {
| |
| list: '/dashboard/api/project_list.php',
| |
| create: '/dashboard/api/project_create.php',
| |
| del: '/dashboard/api/project_delete.php',
| |
| backup: '/dashboard/api/project_backup.php'
| |
| };
| |
| | |
| let currentProject = null;
| |
| | |
| async function loadProjects(){
| |
| const ul = document.getElementById('projectsList');
| |
| if (!ul) return;
| |
| ul.innerHTML = '<li class="muted">Carico…</li>';
| |
| try{
| |
| const r = await fetch(API.list, {credentials:'include', cache:'no-store'});
| |
| const t = await r.text(); let j;
| |
| try{ j = JSON.parse(t); }catch(_){ throw new Error('Risposta non-JSON'); }
| |
| if(!j.ok) throw new Error(j.error||'Lista progetti fallita');
| |
| | |
| ul.innerHTML = '';
| |
| (j.projects||[]).forEach(p=>{
| |
| const li = document.createElement('li');
| |
| li.className = 'list-item';
| |
| const name = document.createElement('strong');
| |
| name.textContent = p.name;
| |
| name.style.fontWeight = (p.name===currentProject?'800':'600');
| |
| | |
| const actions = document.createElement('div'); actions.className='actions';
| |
| const bOpen = document.createElement('button'); bOpen.className='btn'; bOpen.textContent='Apri';
| |
| bOpen.onclick = ()=>{ currentProject = p.name; loadProjects(); logActivity('📁 Progetto selezionato: <b>'+p.name+'</b>'); };
| |
| | |
| const bZip = document.createElement('button'); bZip.className='btn'; bZip.textContent='ZIP';
| |
| bZip.title='Crea backup zip'; bZip.onclick = ()=>backupProject(p.name);
| |
| | |
| const bDel = document.createElement('button'); bDel.className='btn danger'; bDel.textContent='🗑️';
| |
| bDel.title='Sposta nel cestino'; bDel.onclick = ()=>deleteProject(p.name);
| |
| | |
| actions.append(bOpen,bZip,bDel);
| |
| li.append(name, actions);
| |
| ul.appendChild(li);
| |
| });
| |
| | |
| if(!j.projects || !j.projects.length){
| |
| ul.innerHTML = '<li class="muted">Nessun progetto presente.</li>';
| |
| }
| |
| }catch(e){ | |
| ul.innerHTML = '<li class="muted">Errore: '+e.message+'</li>';
| |
| }
| |
| }
| |
| | |
| 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(API.create,{method:'POST',body,credentials:'include'});
| |
| const j = await r.json();
| |
| if(!j.ok) throw new Error(j.error||'create failed');
| |
| input.value=''; currentProject = name;
| |
| logActivity('📁 Creato progetto: <b>'+name+'</b>');
| |
| loadProjects();
| |
| }catch(e){ alert('Errore creazione: '+e.message); }
| |
| }
| |
| | |
| async function deleteProject(name){
| |
| if(!confirm('Spostare "'+name+'" nel cestino?')) return;
| |
| try{
| |
| const body = new URLSearchParams({name});
| |
| const r = await fetch(API.del,{method:'POST',body,credentials:'include'});
| |
| const j = await r.json();
| |
| if(!j.ok) throw new Error(j.error||'delete failed');
| |
| if(currentProject===name) currentProject=null;
| |
| logActivity('🗑️ Progetto nel cestino: <b>'+name+'</b>');
| |
| loadProjects();
| |
| }catch(e){ alert('Errore eliminazione: '+e.message); }
| |
| }
| |
| | |
| async function backupProject(name){
| |
| try{
| |
| const body = new URLSearchParams({name});
| |
| const r = await fetch(API.backup,{method:'POST',body,credentials:'include'});
| |
| const j = await r.json();
| |
| if(!j.ok) throw new Error(j.error||'backup failed');
| |
| alert('Backup creato: '+j.zip);
| |
| logActivity('🗜️ ZIP creato per <b>'+name+'</b>');
| |
| }catch(e){ alert('Errore backup: '+e.message); }
| |
| }
| |
| | |
| /* ---------- test tools ---------- */ | |
| function runTestCode(){
| |
| const ta = document.getElementById('codeArea');
| |
| if(!ta) return;
| |
| try{ new Function(ta.value||'')(); }
| |
| catch(e){ alert('Errore nel codice: '+e.message); }
| |
| }
| |
| | |
| /* ---------- checkCodeAI (usa ai_check.php) ---------- */
| |
| async function checkCodeAI(){
| |
| const codeEl = document.getElementById('codeArea');
| |
| const outEl = document.getElementById('consoleOutput');
| |
| if(!codeEl || !outEl){ alert('Manca #codeArea o #consoleOutput'); return; }
| |
| const code = codeEl.value||'';
| |
| if(!code.trim()) return alert('Incolla del codice da analizzare.');
| |
| | |
| 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:'php', code})
| |
| });
| |
| const j = await r.json();
| |
| if(j.ok){
| |
| let txt=''; if(j.lint && j.lint.trim()) txt+='=== LINT ===\\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; }
| |
| }
| |
| | |
| /* ---------- connessione API test ---------- */
| |
| async function testAPIConnection(){
| |
| const prompt = (document.getElementById('test-prompt')?.value||'').trim();
| |
| const model = document.getElementById('model-select')?.value || 'gpt-4o-2024-05-13';
| |
| const out = document.getElementById('api-result');
| |
| if(!prompt){ out.textContent='⚠️ Inserisci un prompt di test.'; return; }
| |
| out.textContent='⏳ 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){
| |
| out.textContent = '✅ Risposta: ' + data.result;
| |
| logActivity('✅ Proxy OK – modello '+model);
| |
| }else{
| |
| out.textContent = '❌ Errore: ' + (data.error||'risposta vuota');
| |
| }
| |
| }catch(e){ out.textContent='🚫 Errore di rete: '+e.message; }
| |
| }
| |
| | |
| /* ---------- init ---------- */
| |
| document.addEventListener('DOMContentLoaded', ()=>{
| |
| // mostra subito Stato Progetti
| |
| showBox('project-status');
| |
| // bind
| |
| document.getElementById('btnAnalyze')?.addEventListener('click', runProjectAnalysis);
| |
| document.getElementById('btnCreateProject')?.addEventListener('click', createProject);
| |
| // carica lista progetti
| |
| loadProjects();
| |
| });
| |
| </script> | | </script> |
|
| |
|
| </body>
| | |
| </html> | | </html> |