|
|
| (29 versioni intermedie di uno stesso utente non sono mostrate) |
| Riga 1: |
Riga 1: |
| <html> | | <html> |
| <h2>🔧 Dashboard Operativa – Masticationpedia</h2> | | <h2>🔧 Dashboard Operativa – Masticationpedia</h2> |
| <p><em>Centro di comando per progetti, API, file e backup</em></p> | | <p><em>Centro di comando per progetti, API, file e backup</em></p> |
|
| |
|
| <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> | | <a href="/dashboard/api/log_view.php" target="_blank">🧾 Apri Log Dashboard</a> |
| <button onclick="clearServerLog()">🧹 Svuota Log</button> | | <button onclick="clearServerLog()">🧹 Svuota Log</button> |
|
| |
|
| <!-- 🔘 Pulsanti -->
| | <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> |
| <div style="margin: 2rem 0; display: flex; flex-wrap: wrap; gap: 1rem;"> | | <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button> |
| <button class="dashboard-toggle" onclick="toggleDashboardBox('api-settings')">⚙️ Connessione API</button>
| |
| <button class="dashboard-toggle" onclick="toggleDashboardBox('project-status')">📊 Stato Progetti</button>
| |
| <button class="dashboard-toggle" onclick="toggleDashboardBox('chatgpt-plus')">🤖 ChatGPT plus</button>
| |
| <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>
| |
| <button class="dashboard-toggle"
| |
| onclick="(window.showMpAI ? showMpAI() : (function(){var ai=document.getElementById('mpAI'); if(ai) ai.style.display='block';})())">
| |
| 🤖 Masticationpedia AI
| |
| </button> | |
|
| |
|
| </div> | | <button class="dashboard-toggle" |
| | 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'); } })())"> |
| | 🤖 Masticationpedia AI |
| | </button> |
| | </div> |
|
| |
|
| <script> | | <!-- ============== MASTICATIONPEDIA AI ============== --> |
| /* fallback: sempre disponibile e definita presto */
| | <div id="mpAI" class="mpai-root" style="display:none"> |
| window.showMpAI = function(){
| | <div class="mpai-header"> |
| // nascondi eventuali box legacy
| | <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div> |
| ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
| | <div class="mpai-actions"> |
| .forEach(function(id){ var el=document.getElementById(id); if(el) el.style.display='none'; });
| | <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button> |
| // mostra l'AI
| | <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button> |
| var ai = document.getElementById('mpAI');
| | <button id="mpai-save-as-project" class="mpai-btn">💾 Salva come progetto</button> |
| if (ai) { ai.style.display='block'; }
| | </div> |
| else { alert('Elemento #mpAI non trovato nella pagina.'); }
| | </div> |
| };
| |
| </script> | |
|
| |
|
| | <div class="mpai-grid"> |
| | <aside class="mpai-col mpai-left"> |
| | <h4>📂 Progetti</h4> |
| | <div id="mpai-project-list" class="mpai-projects"> |
| | <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> |
|
| |
|
| | <main class="mpai-col mpai-center"> |
| | <div id="mpai-uploads" class="mpai-uploads" data-state="idle"> |
| | <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> |
|
| |
|
| | <div id="mpai-chat" class="mpai-chat"> |
| | <div class="mpai-msg mpai-hint"> |
| | 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. |
| | </div> |
| | </div> |
|
| |
|
| <!-- ============== MASTICATIONPEDIA AI (schermata unica) ============== --> | | <div class="mpai-composer"> |
| <div id="mpAI" class="mpai-root" style="display:none">
| | <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> |
| <!-- Header -->
| |
| <div class="mpai-header">
| |
| <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div>
| |
| <div class="mpai-actions">
| |
| <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button>
| |
| <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button>
| |
| </div>
| |
| </div>
| |
| | |
| <div class="mpai-grid">
| |
| <!-- Sidebar sinistra: Progetti -->
| |
| <aside class="mpai-col mpai-left">
| |
| <h4>📂 Progetti</h4>
| |
| <div id="mpai-project-list" class="mpai-projects">
| |
| <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>
| |
|
| |
|
| <!-- Chat centrale -->
| | <div class="mpai-composer-right"> |
| <main class="mpai-col mpai-center">
| | <label class="mpai-check" style="margin:0 0 6px 0;"> |
| <!-- Allegati (solo elenco locale; invio server lo aggiungiamo in step 2) -->
| | <input id="mpai-drop-urls" type="checkbox"> |
| <div id="mpai-uploads" class="mpai-uploads" data-state="idle">
| | Ignora/Cancella URL e allegati per questo invio |
| <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> | | </label> |
| | <button id="mpai-send" class="mpai-btn primary">Invia</button> |
| </div> | | </div> |
| </div> | | </div> |
| <div id="mpai-file-list" class="mpai-filelist"></div>
| | </main> |
| </div> | |
|
| |
|
| <!-- Messaggi --> | | <aside class="mpai-col mpai-right"> |
| <div id="mpai-chat" class="mpai-chat">
| | <h4>⚙️ Impostazioni</h4> |
| <div class="mpai-msg mpai-hint"> | | <label class="mpai-field">Modello |
| Benvenuto. Seleziona un progetto a sinistra, allega eventuali file <b>sanificati</b> e fai la tua domanda. | | <select id="mpai-model"> |
| Le risposte appariranno qui sotto in ordine, come in ChatGPT. | | <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> |
| | <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> |
| | </select> |
| | </label> |
| | <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> | | </div> |
| </div> | | </aside> |
| | | </div> |
| <!-- Prompt -->
| |
| <div class="mpai-composer">
| |
| <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea>
| |
| <button id="mpai-send" class="mpai-btn primary">Invia</button>
| |
| </div>
| |
| </main> | |
| | |
| <!-- Sidebar destra: Impostazioni -->
| |
| <aside class="mpai-col mpai-right">
| |
| <h4>⚙️ Impostazioni</h4>
| |
| <label class="mpai-field">Modello
| |
| <select id="mpai-model">
| |
| <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
| |
| <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
| |
| </select>
| |
| </label>
| |
| <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>
| |
|
| |
| <!-- ============ STILI (CSS) ============ -->
| |
| <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);}
| |
| .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px}
| |
| .mpai-title{font-size:20px;font-weight:700}
| |
| .mpai-actions{display:flex;gap:8px}
| |
| .mpai-btn{border:1px solid var(--line);background:var(--card);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:var(--card);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}
| |
| @media (max-width: 1100px){
| |
| .mpai-grid{grid-template-columns:1fr}
| |
| .mpai-left,.mpai-right{max-height:none}
| |
| }
| |
| </style>
| |
|
| |
|
| <!-- ============ JS (tutto qui) ============ --> | | <!-- ============ STILI (CSS) ============ --> |
| <script> | | <style> |
| /* -------- Fallback utili (non rompersi se mancano) -------- */
| | .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)} |
| function toggleDashboardBox(id){
| | .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px} |
| var el = document.getElementById(id);
| | .mpai-title{font-size:20px;font-weight:700} |
| if (!el) return;
| | .mpai-actions{display:flex;gap:8px} |
| // nascondo AI se apro altro box
| | .mpai-btn{border:1px solid var(--line);background:#fff;padding:.45rem .8rem;border-radius:8px;cursor:pointer} |
| var ai = document.getElementById('mpAI'); if (ai) ai.style.display='none';
| | .mpai-btn:hover{background:#f3f4f6} |
| el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none';
| | .mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff} |
| } | | .mpai-btn.danger{background:var(--danger);border-color:var(--danger);color:#fff} |
| async function clearServerLog(){
| | .mpai-btn.ghost{background:transparent} |
| try{
| | .muted{color:var(--muted)} |
| const r = await fetch('/dashboard/api/log_clear.php',{method:'POST',credentials:'include'}); | | .mpai-grid{display:grid;grid-template-columns:260px minmax(0,1fr) 260px;gap:12px} |
| const j = await r.json().catch(()=>({ok:false})); | | .mpai-col{background:#fff;border:1px solid var(--line);border-radius:12px;padding:12px} |
| alert(j && j.ok ? 'Log svuotato.' : 'Impossibile svuotare il log.'); | | .mpai-left,.mpai-right{max-height:75vh;overflow:auto} |
| }catch(e){ alert('Errore rete: '+e.message); }
| | .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} |
| function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); }
| | .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} |
|
| |
|
| /* --- mostra la nuova UI AI e nasconde gli altri box --- */ | | /* --- Blocchi codice nelle risposte + copia --- */ |
| function showMpAI(){
| | .mp-code{border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;margin:.5rem 0;background:#fff} |
| ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
| | .mp-code__hdr{display:flex;justify-content:space-between;align-items:center;padding:.5rem .75rem;border-bottom:1px solid #e5e7eb;background:#f8fafc} |
| .forEach(id => { var el = document.getElementById(id); if (el) el.style.display='none'; }); | | .mp-code__lang{font-family:ui-monospace, Menlo, Consolas, monospace;color:#334155} |
| var ai = document.getElementById('mpAI');
| | .mp-copy{border:1px solid #e5e7eb;background:#fff;border-radius:8px;padding:.25rem .5rem;cursor:pointer} |
| if (ai) ai.style.display = 'block';
| | .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> |
|
| |
|
| /* ========= MPAI APP ========= */
| | <!-- ========= SEZIONI LEGACY (nascoste) ========= --> |
| (function(){
| | <div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> |
| // Stato | | <strong>Connessione API (protetta dal server)</strong><br><br> |
| let currentProject = null;
| |
| let attachments = []; // [{file, name}]
| |
| let history = []; // [{role:'user'|'assistant'|'error', content:string}]
| |
|
| |
|
| // Shortcuts
| | <label>Modello</label> |
| const $ = s => document.querySelector(s);
| | <select id="model-select" style="width:100%; margin-bottom:0.5rem;"> |
| const chatEl = $('#mpai-chat');
| | <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option> |
| const fileListEl = $('#mpai-file-list');
| | <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option> |
| | </select> |
|
| |
|
| // UI helpers
| | <label>Prompt di test</label> |
| function appendMsg(role, content){
| | <textarea id="test_prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea> |
| const div = document.createElement('div'); | |
| div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error'));
| |
| div.innerHTML = `<div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div>
| |
| <div class="content" style="white-space:pre-wrap">${content}</div>`;
| |
| chatEl.appendChild(div);
| |
| chatEl.scrollTop = chatEl.scrollHeight;
| |
| }
| |
| function saveLocal(){
| |
| if (!currentProject) return;
| |
| localStorage.setItem('mpai.hist.'+currentProject, JSON.stringify(history.slice(-200)));
| |
| }
| |
| function loadLocal(project){
| |
| const raw = localStorage.getItem('mpai.hist.'+project);
| |
| history = raw ? JSON.parse(raw) : [];
| |
| chatEl.innerHTML = '';
| |
| if (!history.length){
| |
| appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.');
| |
| } else {
| |
| history.forEach(m => appendMsg(m.role, m.content));
| |
| }
| |
| }
| |
| function renderFiles(){
| |
| fileListEl.innerHTML = '';
| |
| attachments.forEach((a,i)=>{
| |
| const pill = document.createElement('span');
| |
| pill.className = 'mpai-filepill';
| |
| pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
| |
| pill.querySelector('button').onclick = ()=>{ attachments.splice(i,1); renderFiles(); };
| |
| fileListEl.appendChild(pill);
| |
| });
| |
| }
| |
|
| |
|
| // Progetti
| | <label style="display:inline-flex; gap:.4rem; align-items:center; margin:.25rem 0 .75rem;"> |
| async function loadProjects(){
| | <input id="test_drop_urls" type="checkbox"> |
| const box = $('#mpai-project-list'); | | Ignora/Cancella URL e allegati per questo invio |
| box.innerHTML = '<em class="muted">Carico progetti…</em>';
| | </label><br> |
| try{
| |
| const r = await fetch('/dashboard/api/project_list.php', {cache:'no-store', credentials:'include'});
| |
| const j = await r.json();
| |
| const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
| |
| if (!arr.length){ box.innerHTML='<em class="muted">Nessun progetto</em>'; return; }
| |
| box.innerHTML='';
| |
| arr.forEach(item=>{
| |
| const name = (typeof item === 'string') ? item : (item.name || item);
| |
| const row = document.createElement('div');
| |
| row.className = 'row';
| |
| row.innerHTML = `<span class="title">${name}</span>
| |
| <span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
| |
| row.querySelector('[data-open]').onclick = ()=>{ currentProject=name; loadLocal(name); };
| |
| box.appendChild(row);
| |
| }); | |
| if (!currentProject){ currentProject = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]); loadLocal(currentProject); }
| |
| }catch(e){ | |
| box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>';
| |
| }
| |
| }
| |
| async function createProject(){
| |
| const inp = $('#mpai-project-name');
| |
| const name = (inp.value||'').trim();
| |
| if (!name){ alert('Inserisci un nome progetto'); return; }
| |
| try{
| |
| const r = await fetch('/dashboard/api/project_create.php', {
| |
| method:'POST',
| |
| headers:{'Content-Type':'application/json'},
| |
| credentials:'include',
| |
| body: JSON.stringify({name})
| |
| });
| |
| const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={ok:false,raw:txt}; }
| |
| if (j.ok!==false){ await loadProjects(); currentProject=name; history=[]; loadLocal(name); inp.value=''; }
| |
| else { alert('Errore creazione: '+(j.error||'')); }
| |
| }catch(e){ alert('Errore rete: '+e.message); }
| |
| }
| |
|
| |
|
| // Upload locale
| | <button id="test_run">Esegui</button> |
| const dz = $('#mpai-dropzone');
| |
| const fi = $('#mpai-file-input');
| |
| dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
| |
| dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
| |
| dz.addEventListener('drop', e => {
| |
| e.preventDefault(); dz.style.opacity = 1;
| |
| Array.from(e.dataTransfer.files||[]).forEach(f=> attachments.push({file:f,name:f.name})); | |
| renderFiles();
| |
| });
| |
| fi.addEventListener('change', () => {
| |
| Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name}));
| |
| fi.value=''; renderFiles();
| |
| });
| |
|
| |
|
| // Invio prompt
| | <pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> |
| async function sendPrompt(){
| | </div> |
| const ta = $('#mpai-input'); | | <script> |
| const model = ($('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
| | // JS minimo temporaneo per sbloccare la pagina |
| const temp = parseFloat($('#mpai-temp')?.value || '0.7') || 0.7;
| | // (serve solo a NON avere errori di sintassi) |
| const sanitizedOnly = $('#mpai-sanitized-only')?.checked;
| |
| const content = (ta.value||'').trim();
| |
| if (!content){ ta.focus(); return; }
| |
| if (!currentProject){ alert('Seleziona o crea un progetto (colonna sinistra).'); return; }
| |
| | |
| history.push({role:'user', content});
| |
| appendMsg('user', content);
| |
| ta.value='';
| |
| | |
| const fileNames = attachments.map(a=>a.name).join(', ');
| |
| const preface = `Contesto progetto: ${currentProject}
| |
| File allegati${sanitizedOnly?' (sanificati)':''}: ${fileNames || 'nessuno'}
| |
| | |
| Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}.
| |
| --- | |
| Utente:
| |
| ${content}`;
| |
| | |
| const pending = document.createElement('div');
| |
| pending.className='mpai-msg'; pending.innerHTML='<em class="muted">Elaboro…</em>';
| |
| chatEl.appendChild(pending); chatEl.scrollTop=chatEl.scrollHeight;
| |
| | |
| try{
| |
| const r = await fetch('/dashboard/api/openai_project_gpt.php', {
| |
| method:'POST',
| |
| headers:{'Content-Type':'application/json'},
| |
| credentials:'include',
| |
| body: JSON.stringify({ model, prompt: preface })
| |
| });
| |
| const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={status:'error',raw:txt}; }
| |
| pending.remove();
| |
| if (j.status==='ok' && j.result){
| |
| history.push({role:'assistant', content:j.result});
| |
| appendMsg('assistant', j.result); saveLocal();
| |
| }else{
| |
| const err = j.error || 'Errore sconosciuto';
| |
| history.push({role:'error', content:err});
| |
| appendMsg('error', err + (j.raw?'\n\nRAW:\n'+j.raw:''));
| |
| saveLocal();
| |
| }
| |
| }catch(e){
| |
| pending.remove(); history.push({role:'error', content:e.message}); appendMsg('error', e.message); saveLocal();
| |
| }
| |
| }
| |
| | |
| // Bind
| |
| document.addEventListener('click', (e)=>{
| |
| if (e.target && e.target.id==='mpai-send') sendPrompt();
| |
| if (e.target && e.target.id==='mpai-project-create') createProject();
| |
| if (e.target && e.target.id==='mpai-new-chat'){
| |
| if (!currentProject){ alert('Crea o seleziona un progetto'); return; }
| |
| history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Nuova chat per "'+currentProject+'".'); saveLocal();
| |
| }
| |
| if (e.target && e.target.id==='mpai-clear'){
| |
| if (!currentProject){ alert('Crea o seleziona un progetto'); return; }
| |
| if (!confirm('Svuotare la conversazione locale?')) return;
| |
| history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Conversazione pulita.'); saveLocal();
| |
| }
| |
| });
| |
| document.addEventListener('keydown', (e)=>{
| |
| if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$('#mpai-input'); if (ta && ta===document.activeElement){ e.preventDefault(); sendPrompt(); } }
| |
| });
| |
| | |
| // Boot
| |
| loadProjects();
| |
| })();
| |
| </script> | | </script> |
| <!-- ============== /MASTICATIONPEDIA AI ============== -->
| |
|
| |
|
| |
| <!-- ========== SEZIONI LEGACY (restano, ma sono nascoste di default) ========== -->
| |
|
| |
| <!-- 📦 Connessione API -->
| |
| <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>
| |
| <label>Modello</label>
| |
| <select id="model-select" style="width:100%; margin-bottom:0.5rem;">
| |
| <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
| |
| <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
| |
| </select>
| |
| <label>Prompt di test</label>
| |
| <textarea id="test-prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea><br>
| |
| <button onclick="(async()=>{try{const r=await fetch('/dashboard/api/openai_project_gpt.php',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'include',body:JSON.stringify({model:document.getElementById('model-select').value,prompt:document.getElementById('test-prompt').value})}); const j=await r.json(); document.getElementById('api-result').textContent=JSON.stringify(j,null,2);}catch(e){document.getElementById('api-result').textContent=e.message;}})()">▶️ 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" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;">
| |
| <strong>📊 Stato Progetti</strong><br><br>
| |
| <div style="margin-top: 1rem;">
| |
| <label><strong>🧠 Risposta GPT – Analisi progetto:</strong></label>
| |
| <div id="gptResponse" class="gpt-card" style="margin-top:8px; background:#fff; border:1px solid #e6e6e6; border-radius:10px; padding:16px; font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; color:#1f2328; box-shadow: 0 1px 2px rgba(0,0,0,.04);">
| |
| <em style="opacity:.7">Qui apparirà la risposta generata da GPT sull’analisi del progetto…</em>
| |
| </div>
| |
| <div 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;">
| |
| <input id="p_goal" placeholder="🎯 Obiettivo (es. mini-corso AI)" />
| |
| <input id="p_audience" placeholder="👥 Pubblico (es. medici, studenti)" />
| |
| <input id="p_deliverable" placeholder="📦 Output atteso (es. 6 capitoli + quiz)" />
| |
| <input id="p_constraints" placeholder="⏱️ Vincoli (tempo, budget, tool)" />
| |
| </div>
| |
| <div style="margin-top:10px;">
| |
| <button id="btnAnalyze" style="background:#0a7cff;color:#fff;border:none;padding:.5rem .9rem;border-radius:8px;font-weight:600;"
| |
| onclick="(function(){const c={goal:document.getElementById('p_goal').value,audience:document.getElementById('p_audience').value,deliverable:document.getElementById('p_deliverable').value,constraints:document.getElementById('p_constraints').value}; const fake=`# Sommario introduttivo
| |
| Obiettivo: ${c.goal||'—'}
| |
| Pubblico: ${c.audience||'—'}
| |
|
| |
| ## Step
| |
| - Pianificazione
| |
| - Esecuzione
| |
| - Revisione
| |
|
| |
| ## Output atteso
| |
| ${c.deliverable||'—'}
| |
|
| |
| ## Vincoli
| |
| ${c.constraints||'—'}`; (function renderGpt(t){const box=document.getElementById('gptResponse'); if(!box){return} let html=(t||'').replace(/\r\n/g,'\n').replace(/^### (.*)$/gm,'<h3 style=\'margin:14px 0 6px;font-size:17px;\'>$1</h3>').replace(/^## (.*)$/gm,'<h2 style=\'margin:16px 0 8px;font-size:19px;\'>$1</h2>').replace(/^# (.*)$/gm,'<h1 style=\'margin:18px 0 10px;font-size:21px;\'>$1</h1>').replace(/^\- (.*)$/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-height:1.5'>${line}</p>`; }).join(''); box.innerHTML=html; })(fake); })()">Analizza con GPT</button>
| |
| </div>
| |
| </div>
| |
| </div>
| |
|
| |
| <div class="projects-controls" style="margin-top:1.5rem; padding:1rem; border:1px solid #ddd; border-radius:6px; background:#fff;">
| |
| <h4>📂 Gestione Progetti</h4>
| |
| <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;"
| |
| onclick="(async()=>{const name=(document.getElementById('newProjectName').value||'').trim(); if(!name){alert('Inserisci un nome'); return;} try{const r=await fetch('/dashboard/api/project_create.php',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'include',body:JSON.stringify({name})}); const t=await r.text(); alert(t.includes('ok')?'Creato':'Vedi log/console');}catch(e){alert(e.message)}})()">Crea progetto</button>
| |
| <ul id="projectsList" class="projects-list" style="margin-top:1rem; list-style:none; padding:0;"></ul>
| |
| </div>
| |
| </div>
| |
|
| |
| <!-- 📘 Registro Attività -->
| |
| <div id="activity-log" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#fdfdfd;">
| |
| <strong>📘 Registro attività:</strong>
| |
| <button onclick="(function(){const box=document.getElementById('activityLogContent'); if(box) box.textContent=''; })()" 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>
| |
|
| |
|
| | |
| </html> | | </html> |