fix: resolve MCP type field switching bug and improve UX feedback across all forms
mcpTypeFields() used getElementById('mt') but radio buttons only had name="mt",
causing all fields to disappear when switching server types. Replaced with
querySelector for checked radio. Also added loading spinners, inline messages
(replacing alert()), and state refresh after async operations in uWallet, uMcp,
lnkMcp, sOci, sGenai, sAdb, sMcp, embedFile, and embedReport.
This commit is contained in:
@@ -347,13 +347,14 @@ ${!S.ociCfg.length?'<div class="emp" style="padding:.85rem"><p>Nenhuma credencia
|
||||
<div class="ig"><label>Compartment OCID</label><input type="text" id="ocp" placeholder="ocid1.compartment.oc1.."></div></div>
|
||||
<div class="g2"><div class="ig"><label>Private Key (.pem)</label><input type="file" id="osk" accept=".pem"></div>
|
||||
<div class="ig"><label>Public Key (.pem)</label><input type="file" id="opk" accept=".pem"></div></div>
|
||||
<button class="btn bp" onclick="sOci()">Salvar Credencial</button></div>`}
|
||||
<button class="btn bp" id="obtn" onclick="sOci()">Salvar Credencial</button></div>`}
|
||||
async function sOci(){const fd=new FormData();fd.append('tenancy_name',document.getElementById('otn').value);fd.append('tenancy_ocid',document.getElementById('oto').value);
|
||||
fd.append('user_ocid',document.getElementById('ouo').value);fd.append('fingerprint',document.getElementById('ofp').value);
|
||||
fd.append('region',document.getElementById('org').value);fd.append('compartment_id',document.getElementById('ocp').value);
|
||||
const sk=document.getElementById('osk').files[0];if(!sk)return sm('om','Selecione a chave privada','e');fd.append('private_key',sk);
|
||||
const pk=document.getElementById('opk').files[0];if(pk)fd.append('public_key',pk);
|
||||
try{await $api('/oci/config',{method:'POST',body:fd,headers:{}});S.ociCfg=await $api('/oci/configs');sm('om','✅ Credencial salva com sucesso!','s');R()}catch(e){sm('om',e.message,'e')}}
|
||||
const btn=document.getElementById('obtn');btn.disabled=true;btn.textContent='Salvando...';sm('om','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Salvando credencial...','i');
|
||||
try{await $api('/oci/config',{method:'POST',body:fd,headers:{}});S.ociCfg=await $api('/oci/configs');sm('om','✅ Credencial salva com sucesso!','s');R()}catch(e){sm('om',e.message,'e');btn.disabled=false;btn.textContent='Salvar Credencial'}}
|
||||
async function tOci(id){try{const d=await $api('/oci/test/'+id,{method:'POST'});alert(d.status==='success'?'✅ Conexão OK!':'❌ '+d.message)}catch(e){alert(e.message)}}
|
||||
async function dOci(id){if(!confirm('Excluir credencial?'))return;await $api('/oci/configs/'+id,{method:'DELETE'});S.ociCfg=await $api('/oci/configs');R()}
|
||||
|
||||
@@ -384,8 +385,8 @@ ${S.ociCfg.map(c=>`<option value="${c.id}">${c.tenancy_name}</option>`).join('')
|
||||
<div class="g3"><div class="ig"><label>Top K</label><input type="number" id="gtk" value="1" min="-1" max="500"></div>
|
||||
<div class="ig"><label>Frequency Penalty</label><input type="number" id="gfp" value="0" step="0.1" min="0" max="2"></div>
|
||||
<div class="ig"><label>Presence Penalty</label><input type="number" id="gpp" value="0" step="0.1" min="0" max="2"></div></div>
|
||||
<button class="btn bp" onclick="sGenai()">Salvar Modelo</button></div>`}
|
||||
async function sGenai(){try{await $api('/genai/config',{method:'POST',body:{
|
||||
<button class="btn bp" id="gbtn" onclick="sGenai()">Salvar Modelo</button></div>`}
|
||||
async function sGenai(){const btn=document.getElementById('gbtn');btn.disabled=true;btn.textContent='Salvando...';sm('gm','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Salvando modelo...','i');try{await $api('/genai/config',{method:'POST',body:{
|
||||
name:document.getElementById('gname').value||'default',
|
||||
oci_config_id:document.getElementById('goci').value,model_id:document.getElementById('gmod').value,
|
||||
model_ocid:document.getElementById('gocid').value||null,
|
||||
@@ -395,12 +396,12 @@ async function sGenai(){try{await $api('/genai/config',{method:'POST',body:{
|
||||
top_p:parseFloat(document.getElementById('gtp').value),top_k:parseInt(document.getElementById('gtk').value),
|
||||
frequency_penalty:parseFloat(document.getElementById('gfp').value),presence_penalty:parseFloat(document.getElementById('gpp').value),
|
||||
is_default:S.genaiCfg.length===0}});
|
||||
S.genaiCfg=await $api('/genai/configs');sm('gm','✅ Modelo salvo com sucesso!','s');R()}catch(e){sm('gm',e.message,'e')}}
|
||||
S.genaiCfg=await $api('/genai/configs');sm('gm','✅ Modelo salvo com sucesso!','s');R()}catch(e){sm('gm',e.message,'e');btn.disabled=false;btn.textContent='Salvar Modelo'}}
|
||||
async function tGenai(id){try{const d=await $api('/genai/test/'+id,{method:'POST'});alert(d.status==='success'?'✅ '+d.message+'\n'+d.response:'❌ '+d.message)}catch(e){alert(e.message)}}
|
||||
async function dGenai(id){if(!confirm('Excluir modelo?'))return;await $api('/genai/configs/'+id,{method:'DELETE'});S.genaiCfg=await $api('/genai/configs');R()}
|
||||
|
||||
/* ── MCP Servers ── */
|
||||
function mcpTypeFields(){const t=document.getElementById('mt')?.value;
|
||||
function mcpTypeFields(){const t=document.querySelector('input[name="mt"]:checked')?.value;
|
||||
document.querySelectorAll('.mcp-f').forEach(el=>{el.style.display='none'});
|
||||
if(t==='stdio'){document.getElementById('mf-cmd').style.display='';document.getElementById('mf-args').style.display=''}
|
||||
if(t==='sse')document.getElementById('mf-url').style.display='';
|
||||
@@ -421,12 +422,13 @@ ${m.command?`<div><span style="color:var(--t3);font-weight:600">Comando:</span>
|
||||
${m.url?`<div><span style="color:var(--t3);font-weight:600">URL:</span> <code style="font-family:var(--fm);font-size:.66rem">${m.url}</code></div>`:''}
|
||||
${m.module_path?`<div><span style="color:var(--t3);font-weight:600">Módulo:</span> <code style="font-family:var(--fm);font-size:.66rem">${m.module_path}</code></div>`:''}
|
||||
<div><span style="color:var(--t3);font-weight:600">ADB:</span> ${adb?`<span class="badge b-pass">${adb.config_name}</span>`:'<span>Nenhum</span>'}</div></div>
|
||||
<div id="mcpm_${m.id}"></div>
|
||||
<div style="display:flex;gap:.5rem;margin-top:.65rem;align-items:center;flex-wrap:wrap">
|
||||
<input type="file" id="muf_${m.id}" accept=".py" style="font-size:.68rem;max-width:200px">
|
||||
<button class="btn bs bsm" onclick="uMcp('${m.id}')">Upload .py</button>
|
||||
<button class="btn bs bsm" id="mub_${m.id}" onclick="uMcp('${m.id}')">Upload .py</button>
|
||||
<select id="mlnk_${m.id}" style="font-size:.72rem;padding:.28rem .5rem;border-radius:6px;border:1px solid var(--bd)">
|
||||
<option value="">Sem ADB</option>${adbOpts}</select>
|
||||
<button class="btn bs bsm" onclick="lnkMcp('${m.id}')">Vincular ADB</button></div></div>`}).join('')
|
||||
<button class="btn bs bsm" id="mlb_${m.id}" onclick="lnkMcp('${m.id}')">Vincular ADB</button></div></div>`}).join('')
|
||||
:'<div class="emp" style="padding:.85rem"><p>Nenhum MCP server registrado.</p></div>'}</div>
|
||||
<div class="cd"><div class="ct">➕ Registrar Novo Server</div>
|
||||
<div class="cdesc">Configure um novo servidor MCP. Os campos se ajustam ao tipo selecionado.</div><div id="mcm"></div>
|
||||
@@ -448,19 +450,21 @@ ${m.module_path?`<div><span style="color:var(--t3);font-weight:600">Módulo:</sp
|
||||
<input type="text" id="margs" placeholder='["--config", "/path/to/config"]'></div></div>
|
||||
<div class="ig" style="margin-top:.5rem"><label>🗄️ Vincular ADB Vector</label><div class="ht">Opcional — o MCP server poderá usar este banco como ferramenta.</div>
|
||||
<select id="madb"><option value="">Nenhum</option>${adbOpts}</select></div>
|
||||
<button class="btn bp" style="margin-top:.75rem" onclick="sMcp()">Registrar Server</button></div>`}
|
||||
async function sMcp(){try{let args=null;const a=document.getElementById('margs')?.value;if(a)try{args=JSON.parse(a)}catch(e){}
|
||||
<button class="btn bp" id="mcbtn" style="margin-top:.75rem" onclick="sMcp()">Registrar Server</button></div>`}
|
||||
async function sMcp(){const btn=document.getElementById('mcbtn');btn.disabled=true;btn.textContent='Registrando...';sm('mcm','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Registrando servidor...','i');try{let args=null;const a=document.getElementById('margs')?.value;if(a)try{args=JSON.parse(a)}catch(e){}
|
||||
const mt=document.querySelector('input[name="mt"]:checked')?.value||'stdio';
|
||||
await $api('/mcp/servers',{method:'POST',body:{name:document.getElementById('mn').value,description:document.getElementById('md').value,
|
||||
server_type:mt,command:document.getElementById('mcmd')?.value||null,url:document.getElementById('murl')?.value||null,args,
|
||||
linked_adb_id:document.getElementById('madb').value||null}});
|
||||
S.mcpSvr=await $api('/mcp/servers');sm('mcm','✅ Registrado!','s');R()}catch(e){sm('mcm',e.message,'e')}}
|
||||
S.mcpSvr=await $api('/mcp/servers');sm('mcm','✅ Registrado!','s');R()}catch(e){sm('mcm',e.message,'e');btn.disabled=false;btn.textContent='Registrar Server'}}
|
||||
async function tgMcp(id){await $api('/mcp/servers/'+id+'/toggle',{method:'PUT'});S.mcpSvr=await $api('/mcp/servers');R()}
|
||||
async function dMcp(id){if(!confirm('Excluir server?'))return;await $api('/mcp/servers/'+id,{method:'DELETE'});S.mcpSvr=await $api('/mcp/servers');R()}
|
||||
async function uMcp(id){const fd=new FormData();const f=document.getElementById('muf_'+id)?.files[0];if(!f)return alert('Selecione um arquivo .py');
|
||||
fd.append('file',f);try{await $api('/mcp/servers/'+id+'/upload',{method:'POST',body:fd,headers:{}});alert('Upload realizado!')}catch(e){alert(e.message)}}
|
||||
async function uMcp(id){const fd=new FormData();const f=document.getElementById('muf_'+id)?.files[0];if(!f)return sm('mcpm_'+id,'Selecione um arquivo .py','e');
|
||||
fd.append('file',f);const btn=document.getElementById('mub_'+id);btn.disabled=true;btn.textContent='Enviando...';sm('mcpm_'+id,'<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Enviando arquivo...','i');
|
||||
try{await $api('/mcp/servers/'+id+'/upload',{method:'POST',body:fd,headers:{}});S.mcpSvr=await $api('/mcp/servers');sm('mcpm_'+id,'✅ Upload realizado!','s');R()}catch(e){sm('mcpm_'+id,e.message,'e');btn.disabled=false;btn.textContent='Upload .py'}}
|
||||
async function lnkMcp(id){const aid=document.getElementById('mlnk_'+id)?.value||'';
|
||||
try{await $api('/mcp/servers/'+id+'/link-adb?adb_id='+aid,{method:'PUT'});S.mcpSvr=await $api('/mcp/servers');alert('Vinculado!');R()}catch(e){alert(e.message)}}
|
||||
const btn=document.getElementById('mlb_'+id);btn.disabled=true;btn.textContent='Vinculando...';
|
||||
try{await $api('/mcp/servers/'+id+'/link-adb?adb_id='+aid,{method:'PUT'});S.mcpSvr=await $api('/mcp/servers');sm('mcpm_'+id,'✅ '+(aid?'Vinculado ao ADB!':'ADB desvinculado!'),'s');R()}catch(e){sm('mcpm_'+id,e.message,'e');btn.disabled=false;btn.textContent='Vincular ADB'}}
|
||||
|
||||
/* ── ADB Vector ── */
|
||||
function rADB(){return`<div class="cd"><div class="ct">🗄️ Conexões Registradas</div>
|
||||
@@ -483,22 +487,23 @@ ${!S.adbCfg.length?'<div class="emp" style="padding:.85rem"><p>Nenhuma conexão
|
||||
<select id="agcfg"><option value="">Nenhum (sem RAG)</option>${S.genaiCfg.map(g=>'<option value="'+g.id+'">'+(g.name||g.model_id)+' · '+g.genai_region+'</option>').join('')}</select></div>
|
||||
<div class="ig"><label>Modelo de Embedding</label><div class="ht">Modelo OCI GenAI para gerar vetores.</div>
|
||||
<select id="aemb">${Object.entries(S.embModels).map(([k,v])=>'<option value="'+k+'">'+v.name+' ('+v.dims+'d)</option>').join('')}</select></div></div>
|
||||
<button class="btn bp" onclick="sAdb()">Salvar Conexão</button></div>
|
||||
<div class="cd"><div class="ct">📤 Upload Wallet</div>
|
||||
<button class="btn bp" id="abtn" onclick="sAdb()">Salvar Conexão</button></div>
|
||||
<div class="cd"><div class="ct">📤 Upload Wallet</div><div id="wm"></div>
|
||||
<div class="g2"><div class="ig"><label>Conexão ADB</label><select id="awsel">${S.adbCfg.map(c=>`<option value="${c.id}">${c.config_name}</option>`).join('')}</select></div>
|
||||
<div class="ig"><label>Wallet ZIP</label><input type="file" id="awf" accept=".zip"></div></div>
|
||||
<button class="btn bs" onclick="uWallet()">Upload Wallet</button></div>`}
|
||||
async function sAdb(){try{await $api('/adb/config',{method:'POST',body:{config_name:document.getElementById('an').value,dsn:document.getElementById('adsn').value,
|
||||
<button class="btn bs" id="wbtn" onclick="uWallet()">Upload Wallet</button></div>`}
|
||||
async function sAdb(){const btn=document.getElementById('abtn');btn.disabled=true;btn.textContent='Salvando...';sm('am','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Salvando conexão...','i');try{await $api('/adb/config',{method:'POST',body:{config_name:document.getElementById('an').value,dsn:document.getElementById('adsn').value,
|
||||
username:document.getElementById('auser').value,password:document.getElementById('apw').value,
|
||||
wallet_password:document.getElementById('awpw').value||null,table_name:document.getElementById('atbl').value,
|
||||
genai_config_id:document.getElementById('agcfg').value||null,
|
||||
embedding_model_id:document.getElementById('aemb').value||'cohere.embed-multilingual-v3.0'}});
|
||||
S.adbCfg=await $api('/adb/configs');sm('am','✅ Conexão salva!','s');R()}catch(e){sm('am',e.message,'e')}}
|
||||
S.adbCfg=await $api('/adb/configs');sm('am','✅ Conexão salva!','s');R()}catch(e){sm('am',e.message,'e');btn.disabled=false;btn.textContent='Salvar Conexão'}}
|
||||
async function tAdb(id){try{const d=await $api('/adb/test/'+id,{method:'POST'});alert(d.status==='success'?'✅ '+d.message:'❌ '+d.message)}catch(e){alert(e.message)}}
|
||||
async function dAdb(id){if(!confirm('Excluir conexão?'))return;await $api('/adb/configs/'+id,{method:'DELETE'});S.adbCfg=await $api('/adb/configs');R()}
|
||||
async function uWallet(){const fd=new FormData();const f=document.getElementById('awf').files[0];if(!f)return alert('Selecione wallet ZIP');
|
||||
fd.append('wallet',f);const id=document.getElementById('awsel').value;if(!id)return alert('Selecione conexão');
|
||||
try{const d=await $api('/adb/'+id+'/upload-wallet',{method:'POST',body:fd,headers:{}});alert('Wallet enviada! Arquivos: '+d.files.join(', '))}catch(e){alert(e.message)}}
|
||||
async function uWallet(){const fd=new FormData();const f=document.getElementById('awf').files[0];if(!f)return sm('wm','Selecione um arquivo wallet ZIP.','e');
|
||||
fd.append('wallet',f);const id=document.getElementById('awsel').value;if(!id)return sm('wm','Selecione uma conexão ADB.','e');
|
||||
const btn=document.getElementById('wbtn');btn.disabled=true;btn.textContent='Enviando...';sm('wm','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Enviando wallet...','i');
|
||||
try{const d=await $api('/adb/'+id+'/upload-wallet',{method:'POST',body:fd,headers:{}});S.adbCfg=await $api('/adb/configs');sm('wm','✅ Wallet enviada! Arquivos: '+d.files.join(', '),'s');R()}catch(e){sm('wm',e.message,'e');btn.disabled=false;btn.textContent='Upload Wallet'}}
|
||||
async function ensureTable(id){try{const d=await $api('/adb/'+id+'/ensure-table',{method:'POST'});alert('Tabela criada/verificada: '+d.table)}catch(e){alert('Erro: '+e.message)}}
|
||||
|
||||
/* ── Embeddings ── */
|
||||
@@ -515,12 +520,12 @@ function rEmbeddings(){
|
||||
<div class="cdesc">Gere embeddings a partir de relatórios CIS. Cada seção (IAM, Networking, etc.) gera um embedding separado.</div><div id="erm"></div>
|
||||
<div class="g2"><div class="ig"><label>Conexão ADB</label><select id="ersel">${adbOpts}</select></div>
|
||||
<div class="ig"><label>Relatório</label><select id="errpt">${rptOpts||'<option value="">Nenhum relatório disponível</option>'}</select></div></div>
|
||||
<button class="btn bp" onclick="embedReport()">Gerar Embeddings do Relatório</button></div>
|
||||
<button class="btn bp" id="erbtn" onclick="embedReport()">Gerar Embeddings do Relatório</button></div>
|
||||
<div class="cd"><div class="ct">📄 Upload de Arquivo</div>
|
||||
<div class="cdesc">Faça upload de um arquivo .txt para gerar embeddings automaticamente (chunking por parágrafos).</div><div id="efm"></div>
|
||||
<div class="g2"><div class="ig"><label>Conexão ADB</label><select id="efsel">${adbOpts}</select></div>
|
||||
<div class="ig"><label>Arquivo (.txt)</label><input type="file" id="eff" accept=".txt"></div></div>
|
||||
<button class="btn bp" onclick="embedFile()">Upload e Gerar Embeddings</button></div>`}
|
||||
<button class="btn bp" id="efbtn" onclick="embedFile()">Upload e Gerar Embeddings</button></div>`}
|
||||
|
||||
async function loadEmbs(){const vid=document.getElementById('elsel').value;if(!vid)return;
|
||||
try{const d=await $api('/embeddings/'+vid+'/list');
|
||||
@@ -533,13 +538,15 @@ async function loadEmbs(){const vid=document.getElementById('elsel').value;if(!v
|
||||
catch(e){document.getElementById('emb-list').innerHTML='<div style="color:var(--rd);padding:.5rem">Erro: '+e.message+'</div>'}}
|
||||
|
||||
async function embedReport(){const vid=document.getElementById('ersel').value;const rid=document.getElementById('errpt').value;
|
||||
if(!vid||!rid)return alert('Selecione conexão ADB e relatório');
|
||||
try{const d=await $api('/embeddings/report/'+rid,{method:'POST',body:{adb_config_id:vid}});sm('erm','✅ '+d.message,'s')}catch(e){sm('erm',e.message,'e')}}
|
||||
if(!vid||!rid)return sm('erm','Selecione conexão ADB e relatório.','e');
|
||||
const btn=document.getElementById('erbtn');btn.disabled=true;btn.textContent='Gerando...';sm('erm','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Gerando embeddings do relatório...','i');
|
||||
try{const d=await $api('/embeddings/report/'+rid,{method:'POST',body:{adb_config_id:vid}});sm('erm','✅ '+d.message,'s');btn.disabled=false;btn.textContent='Gerar Embeddings do Relatório'}catch(e){sm('erm',e.message,'e');btn.disabled=false;btn.textContent='Gerar Embeddings do Relatório'}}
|
||||
|
||||
async function embedFile(){const vid=document.getElementById('efsel').value;const f=document.getElementById('eff').files[0];
|
||||
if(!vid||!f)return alert('Selecione conexão ADB e arquivo');
|
||||
if(!vid||!f)return sm('efm','Selecione conexão ADB e arquivo.','e');
|
||||
const fd=new FormData();fd.append('file',f);fd.append('adb_config_id',vid);
|
||||
try{const d=await $api('/embeddings/upload',{method:'POST',body:fd,headers:{}});sm('efm','✅ '+d.message,'s')}catch(e){sm('efm',e.message,'e')}}
|
||||
const btn=document.getElementById('efbtn');btn.disabled=true;btn.textContent='Enviando...';sm('efm','<span class="spinner" style="display:inline-block;width:14px;height:14px;vertical-align:middle"></span> Enviando e gerando embeddings...','i');
|
||||
try{const d=await $api('/embeddings/upload',{method:'POST',body:fd,headers:{}});sm('efm','✅ '+d.message,'s');btn.disabled=false;btn.textContent='Upload e Gerar Embeddings'}catch(e){sm('efm',e.message,'e');btn.disabled=false;btn.textContent='Upload e Gerar Embeddings'}}
|
||||
|
||||
async function delEmb(vid,docId){if(!confirm('Excluir este embedding?'))return;
|
||||
try{await $api('/embeddings/'+vid+'/'+docId,{method:'DELETE'});loadEmbs()}catch(e){alert('Erro: '+e.message)}}
|
||||
|
||||
Reference in New Issue
Block a user