tuliodisanto's picture
Update templates/index.html
43e8e40 verified
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Buscador de Procedimentos - RN 465 - Vs. Beta com IA</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
/* Melhor Prática: Garante um cálculo de dimensões previsível em todo o site */
*, *::before, *::after {
box-sizing: border-box;
}
:root {
--primary-green: #4A6741;
--accent-green: #5A824F;
--light-green-bg: #EAF2E8;
--text-dark: #333;
--text-light: #666;
--background-page: #f5f7fa;
--background-card: #ffffff;
--border-color: #e0e0e0;
--highlight-color: #fffde7;
--ai-suggestion-color: #8e44ad;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
color: var(--text-dark);
background-color: var(--background-page);
}
.container {
width: 90%;
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: var(--primary-green);
color: white;
padding: 20px 0;
margin-bottom: 30px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
header h1 {
margin: 0;
padding: 0 20px;
font-size: 1.8rem;
text-align: center;
}
.search-container {
background-color: var(--background-card);
padding: 25px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
margin-bottom: 30px;
}
.search-box { display: flex; gap: 10px; }
input[type="text"] {
flex: 1;
padding: 12px 15px;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 16px;
}
input[type="text"]:focus {
border-color: var(--accent-green);
box-shadow: 0 0 0 2px rgba(90, 130, 79, 0.2);
outline: none;
}
button {
background-color: var(--accent-green);
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.2s;
}
button:hover:not(:disabled) { background-color: #4D7044; }
button:disabled { background-color: #9e9e9e; cursor: not-allowed; }
.loader { text-align: center; display: none; margin: 40px 0; }
.loader-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid var(--accent-green);
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#results-summary { font-weight: 500; margin-bottom: 15px; color: var(--text-light); }
.correction-info { background-color: #fffde7; border-left: 5px solid #fdd835; padding: 12px 15px; margin-bottom: 20px; border-radius: 4px; font-size: 0.95em; }
.no-results { text-align: center; color: var(--text-light); padding: 40px; background-color: #fafafa; border-radius: 5px; border: 1px dashed var(--border-color); }
.results-group-title {
margin-top: 30px;
margin-bottom: 15px;
font-size: 1.3rem;
font-weight: 500;
color: var(--primary-green);
border-bottom: 2px solid var(--light-green-bg);
padding-bottom: 8px;
}
.results-group-title:first-child { margin-top: 0; }
.result-item {
border: 1px solid var(--border-color);
border-left: 5px solid var(--accent-green);
border-radius: 6px;
margin-bottom: 20px;
padding: 20px;
background: var(--background-card);
transition: box-shadow 0.3s, border-color 0.3s;
position: relative;
overflow: hidden;
}
.result-item::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 5px;
background-color: var(--accent-green);
z-index: 1;
transition: background-color 0.3s;
}
.result-item:hover { box-shadow: 0 4px 15px rgba(0,0,0,0.08); }
.toggle-details-button {
position: absolute;
right: 0;
bottom: 0;
background-color: var(--primary-green);
color: white;
padding: 5px 15px;
border-top-left-radius: 8px;
font-weight: 500;
font-size: 0.85em;
cursor: pointer;
z-index: 2;
transition: background-color 0.2s;
white-space: nowrap;
}
.toggle-details-button:hover {
background-color: #3b5234;
}
.result-header {
font-weight: bold;
color: var(--primary-green);
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.result-code {
font-family: "Courier New", monospace;
background-color: var(--light-green-bg);
color: var(--primary-green);
padding: 4px 10px;
border-radius: 4px;
font-size: 1em;
}
.scores { display: flex; gap: 15px; font-size: 0.9em; }
.score-item { background-color: #f1f3f5; padding: 3px 10px; border-radius: 12px; font-weight: 500; }
.result-body h3 {
font-size: 1.2em;
color: var(--primary-green);
margin-top: 0;
margin-bottom: 10px;
/* Melhor Prática: Usa Flexbox para um alinhamento robusto */
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.result-body h3.no-coverage {
color: var(--text-light);
font-style: italic;
}
.result-body h3 small {
font-weight: 400;
font-size: 0.8em;
color: var(--text-light);
font-style: normal;
}
.result-body p { margin: 5px 0 15px; font-size: 1em; }
.result-body p strong { color: var(--text-dark); }
.result-body b {
background-color: var(--highlight-color);
font-weight: 600;
padding: 1px 3px;
border-radius: 3px;
}
.info-line {
display: flex;
flex-wrap: wrap;
gap: 10px 25px;
background-color: #fafafa;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
font-size: 0.95em;
}
.info-line-item strong {
display: block;
color: var(--text-light);
font-weight: 500;
margin-bottom: 2px;
}
.info-line a { color: var(--accent-green); text-decoration: none; font-weight: bold; }
.info-line a:hover { text-decoration: underline; }
.section-title {
font-weight: 600;
color: var(--text-dark);
margin-top: 25px;
margin-bottom: 10px;
border-bottom: 2px solid var(--light-green-bg);
padding-bottom: 5px;
}
.synonym-item {
margin-bottom: 12px;
font-size: 0.95em;
}
.synonym-item strong {
color: var(--text-light);
font-weight: 500;
}
.synonym-item p {
margin: 3px 0 0 0;
padding-left: 10px;
border-left: 3px solid #f1f3f5;
}
.collapsible-details {
display: none;
padding-top: 20px;
border-top: 1px solid var(--light-green-bg);
margin: 20px 0;
}
#log-container {
background-color: #f8f9fa;
border-left: 4px solid var(--text-light);
padding: 15px;
margin-top: 40px;
font-family: monospace;
font-size: 0.85em;
color: var(--text-light);
/* Melhor Prática: Garante a quebra de texto inteligente no log */
overflow-wrap: break-word;
}
#log-container h3 { margin-top: 0; }
#log-container ul { padding-left: 20px; margin: 0; }
.footer { text-align: center; margin-top: 40px; padding: 20px; color: #666; font-size: 14px; }
/* Estilos para os links de contato no rodapé */
.contact-links {
margin-top: 15px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap; /* Garante que os links quebrem a linha em telas muito pequenas */
gap: 15px 25px; /* Espaçamento vertical e horizontal entre os links */
}
.contact-link {
display: inline-flex;
align-items: center;
gap: 8px; /* Espaço entre o ícone e o texto */
color: var(--text-light);
text-decoration: none;
transition: color 0.2s ease-in-out;
}
.contact-link:hover {
color: var(--primary-green);
text-decoration: underline;
}
.feedback-button {
background-color: #007bff;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
margin: 20px auto 10px auto;
transition: background-color 0.2s;
display: block;
width: fit-content;
}
.feedback-button:hover:not(:disabled) { background-color: #0056b3; }
.feedback-button:disabled { background-color: #cccccc; cursor: not-allowed; }
.feedback-button.success { background-color: #28a745; cursor: default; }
.feedback-button.error { background-color: #dc3545; cursor: default; }
.user-best-match {
background-color: #ffeb3b;
color: #333;
padding: 3px 8px;
border-radius: 15px;
font-size: 0.75em;
font-weight: bold;
display: inline-flex;
align-items: center;
gap: 5px;
white-space: nowrap;
}
.user-best-match i { color: #fbc02d; font-size: 0.9em; }
.ai-suggestion-container { background-color: var(--light-green-bg); border: 1px solid var(--accent-green); border-radius: 8px; padding: 20px; margin-top: 20px; margin-bottom: 20px; text-align: center; display: none; }
#ai-suggestion-button { background-color: var(--ai-suggestion-color); color: white; font-weight: bold; }
#ai-suggestion-button:hover:not(:disabled) { background-color: #71368a; }
#ai-suggestion-status { margin-top: 15px; font-size: 0.9em; color: var(--text-light); font-weight: 500; }
.result-item[class*="ai-highlight-"] { border-left-color: var(--ai-suggestion-color); box-shadow: 0 0 15px rgba(142, 68, 173, 0.2); }
.result-item[class*="ai-highlight-"]::after { background-color: var(--ai-suggestion-color); }
.result-item[class*="ai-highlight-"] .toggle-details-button { background-color: var(--ai-suggestion-color); }
.result-item[class*="ai-highlight-"] .toggle-details-button:hover { background-color: #71368a; }
.result-item[class*="ai-highlight-"] .result-header { padding-top: 2.5em; }
.result-item[class*="ai-highlight-"]::before {
position: absolute; top: 0; right: 0; color: white;
padding: 5px 15px; font-size: 0.8em; font-weight: bold;
border-bottom-left-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.2);
background-color: var(--ai-suggestion-color);
z-index: 2;
}
.ai-highlight-1::before { content: "1ª Sugestão da IA"; }
.ai-highlight-2::before { content: "2ª Sugestão da IA"; }
.ai-highlight-3::before { content: "3ª Sugestão da IA"; }
.modal {
display: none;
position: fixed;
z-index: 1001;
left: 0; top: 0;
width: 100%; height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.6);
animation: fadeIn 0.3s;
}
.modal-content {
background-color: #fefefe;
margin: 5% auto;
padding: 20px;
border: 1px solid #888;
border-radius: 8px;
width: 80%;
max-width: 800px;
position: relative;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
animation: slideIn 0.3s;
}
@keyframes fadeIn { from {opacity: 0;} to {opacity: 1;} }
@keyframes slideIn { from {top: -50px; opacity: 0} to {top: 0; opacity: 1} }
.modal-header {
padding-bottom: 10px;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
color: var(--primary-green);
}
.modal-close-button {
color: #aaa;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.modal-close-button:hover, .modal-close-button:focus {
color: black;
text-decoration: none;
}
.modal-body h4 {
color: var(--text-dark);
border-bottom: 2px solid var(--light-green-bg);
padding-bottom: 5px;
margin-top: 20px;
margin-bottom: 10px;
}
.modal-body pre, .modal-body code {
background-color: #f4f4f4;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
white-space: pre-wrap;
overflow-wrap: break-word;
font-family: monospace;
font-size: 0.9em;
max-height: 250px;
overflow-y: auto;
}
@media (max-width: 600px) {
.container {
width: 97%;
}
.search-box { flex-direction: column; gap: 15px; }
.search-box button { width: 100%; }
header h1 { font-size: 1.5rem; }
.result-header { flex-direction: column; align-items: flex-start; }
.scores { width: 100%; justify-content: space-between; }
.result-item {
padding-bottom: 40px; /* Cria espaço interno para o botão posicionado na base */
}
.modal-content {
width: 97%; /* Ocupa mais espaço em telas pequenas e funciona corretamente com box-sizing */
}
}
</style>
</head>
<body>
<header><div class="container"><h1>Buscador de Procedimentos - RN 465 - *NÃO Oficial*</h1></div></header>
<div class="container">
<main>
<div class="search-container">
<div class="search-box">
<input type="text" id="search-input" placeholder="Digite um código, procedimento ou termo clínico...">
<button id="search-button">Buscar</button>
</div>
</div>
<div class="loader" id="loader"><div class="loader-spinner"></div><p>Analisando e reordenando resultados...</p></div>
<div id="results-area" style="display: none;">
<div id="results-summary"></div>
<div id="correction-info" class="correction-info" style="display: none;"></div>
<div class="ai-suggestion-container" id="ai-suggestion-container">
<button id="ai-suggestion-button" type="button">
<i class="fas fa-brain"></i> Ver Top 3 Sugestões da IA
</button>
<div id="ai-suggestion-status">A IA pode analisar e reordenar os melhores resultados.</div>
</div>
<div id="results-container"></div>
<div id="log-container"></div>
</div>
</main>
</div>
<div id="ai-reasoning-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>Raciocínio da IA</h2>
<span class="modal-close-button">×</span>
</div>
<div class="modal-body">
<h4>"Pensamento" passo a passo da IA:</h4>
<pre id="ai-thought-process"></pre>
</div>
</div>
</div>
<div class="footer">
<p>Ferramenta de busca com reordenação semântica para o Rol de Procedimentos - Melhoria de resultados com IA.</p>
<div class="contact-links">
<a href="https://www.instagram.com/eu.tuliogoncalves" target="_blank" rel="noopener noreferrer" class="contact-link">
<i class="fa-brands fa-instagram"></i>
<span>@eu.tuliogoncalves</span>
</a>
<a href="mailto:[email protected]" class="contact-link">
<i class="fa-solid fa-envelope"></i>
<span>[email protected]</span>
</a>
</div>
</div>
<script>
const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');
const loader = document.getElementById('loader');
const resultsArea = document.getElementById('results-area');
const resultsContainer = document.getElementById('results-container');
const resultsSummary = document.getElementById('results-summary');
const correctionInfoDiv = document.getElementById('correction-info');
const logContainer = document.getElementById('log-container');
const aiSuggestionContainer = document.getElementById('ai-suggestion-container');
const aiSuggestionButton = document.getElementById('ai-suggestion-button');
const aiSuggestionStatus = document.getElementById('ai-suggestion-status');
const aiReasoningModal = document.getElementById('ai-reasoning-modal');
const closeModalButton = document.querySelector('.modal-close-button');
let lastAIReasoning = null;
let lastSuccessfulSearchData = null;
let lastSearchQuery = '';
let feedbackAlreadySentForCurrentSearch = false;
// INÍCIO DA CORREÇÃO: Adicionar uma função de hash SHA-1 em JavaScript.
async function sha1(str) {
const buf = new TextEncoder("utf-8").encode(str);
const hash = await crypto.subtle.digest("SHA-1", buf);
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
}
// FIM DA CORREÇÃO
function showAIReasoningModal() {
if (!lastAIReasoning) return;
document.getElementById('ai-thought-process').innerHTML = lastAIReasoning.thought_process.replace(/\n/g, '<br>');
aiReasoningModal.style.display = 'block';
}
function hideAIReasoningModal() {
aiReasoningModal.style.display = 'none';
}
closeModalButton.onclick = hideAIReasoningModal;
window.onclick = function(event) {
if (event.target == aiReasoningModal) {
hideAIReasoningModal();
}
}
function resetState() {
feedbackAlreadySentForCurrentSearch = false;
document.querySelectorAll('.feedback-button').forEach(b => {
b.disabled = false;
b.textContent = 'Se este é o seu procedimento, clique aqui!';
b.classList.remove('success', 'error');
});
aiSuggestionContainer.style.display = 'none';
aiSuggestionButton.disabled = false;
aiSuggestionButton.innerHTML = `<i class="fas fa-brain"></i> Ver Top 3 Sugestões da IA`;
aiSuggestionStatus.textContent = 'A IA pode analisar e reordenar os melhores resultados.';
aiSuggestionStatus.style.color = 'var(--text-light)';
document.querySelectorAll('.result-item[class*="ai-highlight-"]').forEach(item => {
item.classList.remove('ai-highlight-1', 'ai-highlight-2', 'ai-highlight-3');
const header = item.querySelector('.result-header');
if (header) {
header.style.paddingTop = '';
}
});
lastSuccessfulSearchData = null;
lastAIReasoning = null;
}
function disableAllFeedbackButtons() {
document.querySelectorAll('.feedback-button').forEach(b => { b.disabled = true; if (!b.classList.contains('success')) b.textContent = 'Feedback Enviado'; });
}
async function performSearch() {
const query = searchInput.value.trim();
if (!query) return;
resultsArea.style.display = 'none';
loader.style.display = 'block';
searchButton.disabled = true;
resetState();
try {
const response = await fetch('/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query }),
});
if (!response.ok) throw new Error(`Erro do servidor: ${response.status}`);
const data = await response.json();
lastSuccessfulSearchData = data;
lastSearchQuery = data.original_query;
await displayResults(data);
if (data.final_semantic_results && data.final_semantic_results.length > 2) {
aiSuggestionContainer.style.display = 'block';
}
} catch (error) {
console.error('Erro na busca:', error);
resultsSummary.textContent = `Ocorreu um erro: ${error.message}.`;
} finally {
loader.style.display = 'none';
resultsArea.style.display = 'block';
searchButton.disabled = false;
}
}
async function displayResults(data) {
resultsContainer.innerHTML = '';
logContainer.innerHTML = '';
correctionInfoDiv.style.display = data.was_corrected ? 'block' : 'none';
if(data.was_corrected) correctionInfoDiv.innerHTML = `<b>NÃO</b> encontramos resultados para "<b>${data.original_query}</b>". A busca foi refeita com: "<b>${data.corrected_query}</b>".`;
const results = data.final_semantic_results || [];
if (results.length === 0) {
resultsSummary.textContent = 'Nenhum resultado encontrado.';
resultsContainer.innerHTML = '<div class="no-results">Tente usar outros termos.</div>';
} else {
resultsSummary.innerHTML = `Exibindo os <strong> ${results.length} </strong> resultados mais relevantes de uma busca abrangente. Para melhorar a identificação, localize o procedimento com base no título e Síntese Semântica. <strong> Procure os canais oficiais para uma resposta técnica. </strong>`;
const titleRelevant = document.createElement('h2');
titleRelevant.className = 'results-group-title';
titleRelevant.textContent = 'Resultados da Busca';
resultsContainer.appendChild(titleRelevant);
// INÍCIO DA CORREÇÃO: Usar Promise.all para criar os cards de forma assíncrona
const cardPromises = results.map(result => createResultCardElement(result, data.original_query));
const cards = await Promise.all(cardPromises);
cards.forEach(card => resultsContainer.appendChild(card));
// FIM DA CORREÇÃO
}
const logList = document.createElement('ul');
data.search_log.forEach(logItem => { const li = document.createElement('li'); li.textContent = logItem; logList.appendChild(li); });
logContainer.innerHTML = '<h3>Log da Busca</h3>';
logContainer.appendChild(logList);
}
async function createResultCardElement(result, originalQuery) {
const itemDiv = document.createElement('div');
itemDiv.className = 'result-item';
// INÍCIO DA CORREÇÃO: Adicionar ID único ao elemento do card
itemDiv.dataset.tussCode = result.Codigo_TUSS;
const procedimentoRolStr = result.Procedimento_Rol || '';
const hash = await sha1(procedimentoRolStr);
itemDiv.dataset.uniqueId = `${result.Codigo_TUSS}_${hash.substring(0, 8)}`;
// FIM DA CORREÇÃO
const codigosInvalidos = [ '11111111', '22222222', '33333333', '44444444', '55555555', '66666666', '77777777', '88888888', '99999999' ];
const header = document.createElement('div');
header.className = 'result-header';
header.innerHTML = `
<span class="result-code"><strong>Cód. TUSS:</strong> ${ !result.Codigo_TUSS || codigosInvalidos.includes(result.Codigo_TUSS) ? 'Não existe' : result.Codigo_TUSS }</span>
<div class="scores">
<span class="score-item" title="Score da busca semântica (relevância do significado)">Semântico: <b>${result.semantic_score || 0}%</b></span>
<span class="score-item" title="Score da busca inicial por texto (fuzzy/lógica)">Texto: <b>${result.score || 0}%</b></span>
</div>`;
itemDiv.appendChild(header);
const bodyDiv = document.createElement('div');
bodyDiv.className = 'result-body';
const titleH3 = document.createElement('h3');
let userBestMatchTag = '';
if (result.is_user_best_match) {
userBestMatchTag = '<span class="user-best-match"><i class="fas fa-star"></i> Melhor correspondência segundo usuários</span>';
}
if (!result.Procedimento_Rol || result.Procedimento_Rol.trim() === '---') {
titleH3.className = 'no-coverage';
titleH3.innerHTML = `<span>O procedimento não tem cobertura pelo Rol da ANS</span> ${userBestMatchTag}`;
} else {
let vigenciaText = (result.Vigencia && result.Vigencia.trim() !== '' && result.Vigencia.trim() !== '---') ? `(A partir da RN ${result.Resolucao_Normativa || 'especificada'})` : '(RN 465/2021)';
titleH3.innerHTML = `<span><strong>Descrição Rol:</strong> ${result.Procedimento_Rol_highlighted}</span> <small>${vigenciaText}</small> ${userBestMatchTag}`;
}
bodyDiv.appendChild(titleH3);
const pTuss = document.createElement('p');
pTuss.innerHTML = `<strong>Descrição TUSS:</strong> ${result.Descricao_TUSS_highlighted || 'Não existe'}`;
bodyDiv.appendChild(pTuss);
const infoLine = document.createElement('div');
infoLine.className = 'info-line';
const pacText = result.PAC || '';
let dutText = result.DUT || 'Não';
if (result.DUT && result.DUT.trim() !== '' && result.DUT.trim().toLowerCase() !== 'não') {
dutText = `<a href="https://www.ans.gov.br/images/stories/Legislacao/rn/Anexo_II_DUT_2021_RN_465.2021_RN637.2025.pdf" target="_blank" rel="noopener noreferrer">${result.DUT}</a>`;
}
infoLine.innerHTML = `
<div class="info-line-item"><strong>PAC</strong><span>${pacText}</span></div>
<div class="info-line-item"><strong>DUT</strong><span>${dutText}</span></div>
<div class="info-line-item"><strong>Grupo</strong><span>${result.GRUPO || '-'}</span></div>
<div class="info-line-item"><strong>Subgrupo</strong><span>${result.SUBGRUPO || '-'}</span></div>`;
bodyDiv.appendChild(infoLine);
const segmentationTitle = document.createElement('h4');
segmentationTitle.className = 'section-title';
segmentationTitle.textContent = 'Cobertura nas seguintes segmentações de plano:';
bodyDiv.appendChild(segmentationTitle);
const segmentationP = document.createElement('p');
const segmentationMap = {
'OD': 'Odontológico',
'AMB': 'Ambulatorial',
'HCO': 'Hospitalar Com Obstetrícia',
'HSO': 'Hospitalar Sem Obstetrícia'
};
const segmentKeys = ['OD', 'AMB', 'HCO', 'HSO'];
const segments = segmentKeys
.filter(key => result[key] && result[key].trim() !== '' && result[key].trim() !== '---')
.map(key => segmentationMap[key]);
segmentationP.textContent = (segments.length > 0) ? segments.join(', ') : 'O procedimento não está previsto no Rol de Procedimentos Editado pela ANS.';
bodyDiv.appendChild(segmentationP);
if (result.Semantico_highlighted && result.Semantico_highlighted.trim() !== '' && result.Semantico_highlighted.trim() !== 'N/A') {
const sinteseDiv = document.createElement('div');
sinteseDiv.className = 'synonym-item';
const p = document.createElement('p');
p.innerHTML = result.Semantico_highlighted;
sinteseDiv.innerHTML = `<strong>Síntese Semântica:</strong>`;
sinteseDiv.appendChild(p);
bodyDiv.appendChild(sinteseDiv);
}
itemDiv.appendChild(bodyDiv);
const collapsibleDetailsDiv = document.createElement('div');
collapsibleDetailsDiv.className = 'collapsible-details';
const synonymsToShow = [
{ title: 'Sinônimo 1 (Popular/Explicativo)', content: result.Sinonimo_1_highlighted }, { title: 'Sinônimo 2 (Abreviação/Técnico)', content: result.Sinonimo_2_highlighted },
{ title: 'Sinônimo 3 (Entre Tabelas)', content: result.Sinonimo_3_highlighted }, { title: 'Sinônimo 4 (Outros)', content: result.Sinonimo_4_highlighted },
];
const hasSynonyms = synonymsToShow.some(item => item.content && item.content.trim() !== '' && item.content.trim() !== 'N/A');
if (hasSynonyms) {
const synonymTitle = document.createElement('h4');
synonymTitle.className = 'section-title';
synonymTitle.textContent = 'Outros Sinônimos';
collapsibleDetailsDiv.appendChild(synonymTitle);
synonymsToShow.forEach(item => {
if (item.content && item.content.trim() !== '' && item.content.trim() !== 'N/A') {
const synonymDiv = document.createElement('div');
synonymDiv.className = 'synonym-item';
const p = document.createElement('p');
p.innerHTML = item.content;
synonymDiv.innerHTML = `<strong>${item.title}:</strong>`;
synonymDiv.appendChild(p);
collapsibleDetailsDiv.appendChild(synonymDiv);
}
});
}
itemDiv.appendChild(collapsibleDetailsDiv);
const feedbackButton = document.createElement('button');
feedbackButton.className = 'feedback-button';
feedbackButton.textContent = 'Se este é o seu procedimento, clique aqui!';
feedbackButton.onclick = () => submitFeedback(originalQuery, result.Codigo_TUSS, feedbackButton);
itemDiv.appendChild(feedbackButton);
if (hasSynonyms) {
const toggleButton = document.createElement('div');
toggleButton.className = 'toggle-details-button';
toggleButton.textContent = 'Ver mais sinônimos ↓';
itemDiv.appendChild(toggleButton);
toggleButton.addEventListener('click', (e) => {
e.stopPropagation();
const isExpanded = collapsibleDetailsDiv.style.display === 'block';
collapsibleDetailsDiv.style.display = isExpanded ? 'none' : 'block';
toggleButton.textContent = isExpanded ? 'Ver mais sinônimos ↓' : 'Ver menos sinônimos ↑';
});
}
return itemDiv;
}
async function submitFeedback(originalQuery, tussCodeSubmitted, buttonElement) {
if (feedbackAlreadySentForCurrentSearch) return;
if (!confirm(`Confirma que "${tussCodeSubmitted}" é a melhor correspondência para "${originalQuery}"?`)) return;
if(buttonElement) buttonElement.disabled = true;
try {
const response = await fetch('/submit_feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: originalQuery, tuss_code: tussCodeSubmitted, feedback_type: 'click_result', tuss_code_raw_input: '' })
});
if (!response.ok) throw new Error('Falha no envio do feedback.');
feedbackAlreadySentForCurrentSearch = true;
if (buttonElement) { buttonElement.textContent = 'Enviado!'; buttonElement.classList.add('success'); }
disableAllFeedbackButtons();
} catch (error) {
if (buttonElement) { buttonElement.textContent = 'Erro!'; buttonElement.classList.add('error'); buttonElement.disabled = false; }
}
}
async function getAiSuggestions() {
if (lastAIReasoning) {
showAIReasoningModal();
return;
}
if (!lastSuccessfulSearchData || !lastSuccessfulSearchData.final_semantic_results || lastSuccessfulSearchData.final_semantic_results.length === 0) {
console.warn("Sem dados da última busca para enviar à IA.");
return;
}
const queryForAI = lastSuccessfulSearchData.corrected_query || lastSuccessfulSearchData.original_query;
const resultsForAI = lastSuccessfulSearchData.final_semantic_results;
aiSuggestionButton.disabled = true;
aiSuggestionStatus.textContent = 'A IA está analisando os resultados...';
aiSuggestionButton.innerHTML = `<div class="loader-spinner" style="width:18px; height:18px; border-top-color: white; margin: 0 auto;"></div>`;
try {
const response = await fetch('/get_ai_suggestion', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: queryForAI, results: resultsForAI })
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || 'Erro desconhecido da IA.');
lastAIReasoning = data;
// INÍCIO DA CORREÇÃO: Usar 'suggested_ids' para reordenar
if (data.suggested_ids && data.suggested_ids.length > 0) {
reorderResults(data.suggested_ids);
aiSuggestionStatus.textContent = `As 3 melhores sugestões da IA foram reordenadas no topo da lista.`;
aiSuggestionStatus.style.color = 'var(--primary-green)';
aiSuggestionButton.innerHTML = `<i class="fas fa-comment-dots"></i> Ver "raciocínio" da IA`;
aiSuggestionButton.disabled = false;
} else {
throw new Error('A IA não retornou sugestões de IDs válidas.');
}
// FIM DA CORREÇÃO
} catch (error) {
console.error("Erro ao buscar sugestões da IA:", error);
aiSuggestionStatus.textContent = `Erro: ${error.message}`;
aiSuggestionStatus.style.color = '#dc3545';
aiSuggestionButton.disabled = false;
aiSuggestionButton.innerHTML = `<i class="fas fa-brain"></i> Ver Top 3 Sugestões da IA`;
}
}
function reorderResults(suggestedIds) {
document.querySelectorAll('.result-item[class*="ai-highlight-"]').forEach(item => {
item.classList.remove('ai-highlight-1', 'ai-highlight-2', 'ai-highlight-3');
});
const topSuggestions = [];
// INÍCIO DA CORREÇÃO: Usar o 'data-unique-id' para encontrar o elemento correto
suggestedIds.forEach((id, index) => {
const item = document.querySelector(`.result-item[data-unique-id="${id}"]`);
if (item) {
item.classList.add(`ai-highlight-${index + 1}`);
topSuggestions.push(item);
} else {
console.warn(`Item com ID único '${id}' não encontrado no DOM.`);
}
});
// FIM DA CORREÇÃO
topSuggestions.reverse().forEach(item => {
resultsContainer.prepend(item);
});
}
aiSuggestionButton.addEventListener('click', getAiSuggestions);
searchButton.addEventListener('click', performSearch);
searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') performSearch(); });
searchInput.focus();
</script>
</body>
</html>