From edc4652b35fd6e0a28557fa6c6376e12363a0976 Mon Sep 17 00:00:00 2001 From: nogueiraguh Date: Tue, 3 Mar 2026 14:44:46 -0300 Subject: [PATCH] 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. --- frontend/index.html | 63 +++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 749e6b1..98fff9e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -347,13 +347,14 @@ ${!S.ociCfg.length?'

Nenhuma credencia

-`} +`} 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',' 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=>``).join('')
-`} -async function sGenai(){try{await $api('/genai/config',{method:'POST',body:{ +`} +async function sGenai(){const btn=document.getElementById('gbtn');btn.disabled=true;btn.textContent='Salvando...';sm('gm',' 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?`
Comando: ${m.url?`
URL: ${m.url}
`:''} ${m.module_path?`
Módulo: ${m.module_path}
`:''}
ADB: ${adb?`${adb.config_name}`:'Nenhum'}
+
- + -
`}).join('') +`}).join('') :'

Nenhum MCP server registrado.

'}
➕ Registrar Novo Server
Configure um novo servidor MCP. Os campos se ajustam ao tipo selecionado.
@@ -448,19 +450,21 @@ ${m.module_path?`
Módulo:
Opcional — o MCP server poderá usar este banco como ferramenta.
-`} -async function sMcp(){try{let args=null;const a=document.getElementById('margs')?.value;if(a)try{args=JSON.parse(a)}catch(e){} +`} +async function sMcp(){const btn=document.getElementById('mcbtn');btn.disabled=true;btn.textContent='Registrando...';sm('mcm',' 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,' 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`
🗄️ Conexões Registradas
@@ -483,22 +487,23 @@ ${!S.adbCfg.length?'

Nenhuma conexão

Modelo OCI GenAI para gerar vetores.
- -
📤 Upload Wallet
+
+
📤 Upload Wallet
-
`} -async function sAdb(){try{await $api('/adb/config',{method:'POST',body:{config_name:document.getElementById('an').value,dsn:document.getElementById('adsn').value, +`} +async function sAdb(){const btn=document.getElementById('abtn');btn.disabled=true;btn.textContent='Salvando...';sm('am',' 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',' 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(){
Gere embeddings a partir de relatórios CIS. Cada seção (IAM, Networking, etc.) gera um embedding separado.
- +
📄 Upload de Arquivo
Faça upload de um arquivo .txt para gerar embeddings automaticamente (chunking por parágrafos).
-
`} +`} 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='
Erro: '+e.message+'
'}} 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',' 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',' 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)}}