2025-05-26 14:22:19 +02:00
<!DOCTYPE html>
< html lang = "pt-BR" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2025-06-16 13:11:57 -04:00
< title > Diários Oficiais< / title >
2025-05-26 14:22:19 +02:00
<!-- Bootstrap 5 CSS -->
2025-06-16 13:11:57 -04:00
< link href = "./css/bootstrap-custom.min.css" rel = "stylesheet" >
2025-05-26 14:22:19 +02:00
<!-- Bootstrap Icons -->
2025-06-16 13:11:57 -04:00
< link rel = "stylesheet" href = "./css/bootstrap-icons.css" >
2025-05-26 14:22:19 +02:00
<!-- Alpine.js -->
2025-06-16 13:11:57 -04:00
< script defer src = "./js/alpine.min.js" > < / script >
2025-05-26 14:22:19 +02:00
<!-- Estilos customizados -->
2025-06-16 13:11:57 -04:00
< link rel = "stylesheet" href = "./css/styles.css" >
< link rel = "icon" href = "./img/favicon.ico" type = "image/x-icon" >
2025-05-26 14:22:19 +02:00
< / head >
< body class = "bg-light-gradient" >
2025-06-16 13:11:57 -04:00
< div class = "container py-5" x-data = "searchApp" > <!-- Container do Logo e Imagem Direita -->
< div class = "row justify-content-center mb-4" >
< div class = "col-12 col-lg-10" >
< div class = "d-flex justify-content-between align-items-center mb-5" >
<!-- Logo à esquerda -->
< div class = "logo-container" >
< img src = "img/logo.jpg" alt = "Logo" class = "img-fluid" >
< / div >
<!-- Imagem à direita -->
< div class = "risco-container" >
< img src = "img/risco.jpg" alt = "Risco" class = "img-fluid" >
< / div >
< / div >
< / div >
2025-05-26 14:22:19 +02:00
< div class = "row justify-content-center mb-4" >
< div class = "col-12 col-lg-10" >
< div class = "text-center mb-5" >
< h1 class = "display-5 fw-bold text-primary mb-3" >
2025-06-16 13:11:57 -04:00
< i class = "bi bi-search me-2" > < / i > Diários Oficiais
2025-05-26 14:22:19 +02:00
< / h1 >
< / div >
< div class = "card search-card mb-5" >
< div class = "card-body p-4" >
< form @ submit . prevent = "performSearch" class = "row g-3" >
< div class = "col-12" >
< div class = "input-group" >
< span class = "input-group-text" > < i class = "bi bi-search" > < / i > < / span >
< input type = "text" class = "form-control form-control-lg"
x-model="searchParams.q"
placeholder="Digite o termo de busca"
aria-label="Termo de busca">
< button class = "btn btn-primary" type = "submit" >
< span x-show = "!isLoading" > Buscar< / span >
< span x-show = "isLoading" class = "spinner-border spinner-border-sm" role = "status" aria-hidden = "true" > < / span >
< / button >
< / div >
< / div >
<!-- Opções básicas de busca (sempre visíveis) -->
< div class = "col-12 mt-2" >
< div class = "row align-items-end" >
< div class = "col-md-4 mb-2 mb-md-0" >
< label for = "numero_diario" class = "form-label" > Número do Diário< / label >
< input type = "text" class = "form-control" id = "numero_diario"
x-model="searchParams.numero_diario"
placeholder="Ex: 1234">
< / div >
< div class = "col-md-4 mb-2 mb-md-0" >
< label for = "modo_busca" class = "form-label" > Modo de Busca< / label >
< select class = "form-select" id = "modo_busca" x-model = "searchParams.modo_busca" >
< option value = "exata" > Busca exata< / option >
< option value = "qualquer" > Qualquer termo< / option >
< / select >
< / div >
< div class = "col-md-6 col-xl-4" >
< div class = "d-flex flex-column" >
< label class = "form-label" > Ordenar por< / label >
< div class = "btn-group" role = "group" >
< button type = "button" class = "btn btn-outline-secondary btn-sm"
:class="{'active': searchParams.ordenar_por === 'relevancia'}"
@click="changeOrder('relevancia')">
< i class = "bi bi-star me-1" > < / i > Relevância
< / button >
< button type = "button" class = "btn btn-outline-secondary btn-sm"
:class="{'active': searchParams.ordenar_por === 'data_desc'}"
@click="changeOrder('data_desc')">
< i class = "bi bi-sort-down-alt me-1" > < / i > Data< br > (Decrescente)
< / button >
< button type = "button" class = "btn btn-outline-secondary btn-sm"
:class="{'active': searchParams.ordenar_por === 'data_asc'}"
@click="changeOrder('data_asc')">
< i class = "bi bi-sort-down me-1" > < / i > Data< br > (Crescente)
< / button >
< / div >
< / div >
< / div >
< / div >
< / div >
< div class = "col-12 mt-3" >
< button class = "btn btn-sm btn-outline-secondary" type = "button" @ click = "showAdvanced = !showAdvanced" >
< span x-text = "showAdvanced ? 'Ocultar filtros avançados' : 'Mostrar filtros avançados'" > < / span >
< i class = "bi" :class = "showAdvanced ? 'bi-chevron-up' : 'bi-chevron-down'" > < / i >
< / button >
< / div >
< div class = "col-12" x-show = "showAdvanced" x-transition >
< div class = "row g-3 mt-1" >
< div class = "col-md-6" >
< label for = "data_inicio" class = "form-label" > Data inicial< / label >
< input type = "date" class = "form-control date-picker" id = "data_inicio" x-model = "searchParams.data_inicio" >
< / div >
< div class = "col-md-6" >
< label for = "data_fim" class = "form-label" > Data final< / label >
< input type = "date" class = "form-control date-picker" id = "data_fim" x-model = "searchParams.data_fim" >
< / div >
< / div >
< div class = "row g-3 mt-1" >
< div class = "col-md-6" >
< label for = "page_size" class = "form-label" > Resultados por página< / label >
< select class = "form-select" id = "page_size" x-model = "searchParams.page_size" >
< option value = "10" > 10< / option >
< option value = "20" > 20< / option >
< option value = "30" > 30< / option >
< option value = "50" > 50< / option >
< / select >
< / div >
< / div >
< / div >
< / form >
< / div >
< / div >
<!-- Seção Você quis dizer -->
< template x-if = "!isLoading && !error && searchResults && suggestion && shouldShowSuggestion" >
< div class = "mb-3 mt-3 alert alert-info d-flex align-items-center" >
< i class = "bi bi-lightbulb-fill me-2" > < / i >
< span > Você quis dizer:
< a href = "#" @ click . prevent = "usesuggestion" class = "alert-link" x-text = "suggestion" > < / a > ?
< / span >
< / div >
< / template >
<!-- Resultados -->
< div x-show = "hasSearched" class = "mb-4" >
< template x-if = "isLoading" >
< div class = "d-flex justify-content-center my-5" >
< div class = "spinner-border text-primary" role = "status" >
< span class = "visually-hidden" > Carregando...< / span >
< / div >
< / div >
< / template >
< template x-if = "!isLoading && error" >
< div class = "alert alert-danger" role = "alert" >
< i class = "bi bi-exclamation-triangle-fill me-2" > < / i >
< span x-text = "error" > < / span >
< / div >
< / template >
< template x-if = "!isLoading && !error && searchResults" >
< div >
< div class = "d-flex justify-content-between align-items-center mb-4" >
< h2 class = "h4 m-0" >
< span x-text = "searchResults.total" > < / span > Resultados encontrados
< template x-if = "searchParams.q" >
< span > para "< span x-text = "searchParams.q" > < / span > "< / span >
< / template >
< / h2 >
< button @ click = "resetSearch" class = "btn btn-sm btn-outline-secondary" >
< i class = "bi bi-arrow-counterclockwise me-1" > < / i > Nova busca
< / button >
< / div >
< template x-if = "searchResults.total === 0" >
< div class = "alert alert-info" role = "alert" >
< i class = "bi bi-info-circle-fill me-2" > < / i >
Nenhum resultado encontrado para os critérios de busca informados.
< / div >
< / template >
< template x-if = "searchResults.total > 0" >
< div >
<!-- Lista de resultados -->
< div class = "mb-4" >
< template x-for = "(diario, diarioIndex) in searchResults.resultados" :key = "diario.id" >
< div class = "card result-card mb-4 border-0 shadow-sm" >
< div class = "card-header bg-white py-3 d-flex justify-content-between align-items-center" >
< h5 class = "card-title mb-0" >
< span class = "badge bg-primary me-2" x-text = "diario.tipo" > < / span >
< span x-text = "diario.numero" > < / span >
< / h5 >
< span class = "text-muted" x-text = "formatDate(diario.data)" > < / span >
< / div >
< div class = "card-body" >
< template x-if = "diario.paginas && diario.paginas.length > 0" >
< div >
<!-- Melhor página encontrada (mostrada apenas se estiver ordenado por relevância e tiver score) -->
< template x-if = "hasBestMatch(diario)" >
< div class = "mb-4 p-3 best-match rounded" >
< div class = "d-flex justify-content-between align-items-center mb-2" >
< div >
< span class = "page-badge me-2" > Página < span x-text = "getBestPage(diario.paginas).numero" > < / span > < / span >
< span class = "match-score" >
< i class = "bi bi-star-fill me-1 small" > < / i >
Melhor correspondência no diário
< / span >
< / div >
< / div >
< div class = "page-content" >
< div x-html = "getBestPage(diario.paginas).conteudo" > < / div >
< / div >
< / div >
< / template >
<!-- Accordion de páginas -->
< template x-if = "diario.paginas.length > 1" >
< div class = "accordion mt-3" :id = "'accordionDiario' + diario.id" >
< div class = "accordion-item border-0 mb-2" >
< h2 class = "accordion-header" >
< button class = "accordion-button collapsed shadow-sm" type = "button"
data-bs-toggle="collapse"
:data-bs-target="'#collapse' + diario.id"
aria-expanded="false">
< i class = "bi bi-list-ul me-2" > < / i >
< template x-if = "hasBestMatch(diario) && diario.paginas.length > 1" >
< span > Ver mais < span class = "mx-1" x-text = "getOtherPages(diario.paginas).length" > < / span > páginas deste diário< / span >
< / template >
< template x-if = "!hasBestMatch(diario) && diario.paginas.length > 1" >
< span > Ver < span class = "mx-1" x-text = "diario.paginas.length" > < / span > páginas deste diário< / span >
< / template >
< / button >
< / h2 >
< div :id = "'collapse' + diario.id" class = "accordion-collapse collapse"
:data-bs-parent="'#accordionDiario' + diario.id">
< div class = "accordion-body p-0" >
< div class = "list-group list-group-flush" >
< template x-for = "pagina in hasBestMatch(diario) ? getOtherPages(diario.paginas) : diario.paginas" :key = "pagina.numero" >
< div class = "list-group-item border-0 py-3" >
< div class = "d-flex justify-content-between align-items-center mb-2" >
< span class = "page-badge" > Página < span x-text = "pagina.numero" > < / span > < / span >
< / div >
< div class = "page-preview" x-html = "pagina.conteudo.substring(0, 200) + '...'" > < / div >
< div x-show = "isFullContentVisible(diarioIndex, pagina.numero)" x-transition class = "mt-2 page-content border-top pt-3" >
< div x-html = "pagina.conteudo" > < / div >
< / div >
< / div >
< / template >
< / div >
< / div >
< / div >
< / div >
< / template >
< / div >
< / div >
< / template >
< div class = "d-flex justify-content-end mt-3" >
< a :href = "diario.link" target = "_blank" class = "btn btn-sm btn-outline-primary" >
< i class = "bi bi-file-earmark-pdf me-1" > < / i > Ver Diário Completo
< / a >
< / div >
< / div >
< / div >
< / template >
< / div >
<!-- Paginação -->
< nav aria-label = "Navegação de páginas" x-show = "totalPages > 1" >
< ul class = "pagination justify-content-center" >
< li class = "page-item" :class = "{ 'disabled': searchParams.page <= 1 }" >
< a class = "page-link" href = "#" @ click . prevent = "goToPage(searchParams.page - 1)" aria-label = "Anterior" >
< span aria-hidden = "true" > « < / span >
< / a >
< / li >
< template x-for = "page in paginationArray" :key = "page" >
< li class = "page-item" :class = "{ 'active': page === searchParams.page }" >
< a class = "page-link" href = "#" @ click . prevent = "goToPage(page)" x-text = "page" > < / a >
< / li >
< / template >
< li class = "page-item" :class = "{ 'disabled': searchParams.page >= totalPages }" >
< a class = "page-link" href = "#" @ click . prevent = "goToPage(searchParams.page + 1)" aria-label = "Próximo" >
< span aria-hidden = "true" > » < / span >
< / a >
< / li >
< / ul >
< / nav >
< / div >
< / template >
< / div >
< / template >
< / div >
< / div >
< / div >
< / div >
<!-- Footer -->
< footer class = "bg-dark text-white py-4 mt-5" >
< div class = "container" >
< div class = "row" >
< div class = "col-md-6" >
2025-06-16 13:11:57 -04:00
< h5 > Diários Oficiais< / h5 >
2025-05-26 14:22:19 +02:00
< / div >
< div class = "col-md-6 text-md-end" >
< p class = "small mb-0" > © 2025 Todos os direitos reservados< / p >
< / div >
< / div >
< / div >
< / footer >
2025-06-16 13:11:57 -04:00
<!-- Botão de ajuda que abre o modal -->
< button type = "button" class = "btn btn-outline-secondary position-fixed bottom-0 end-0 m-3" data-bs-toggle = "modal" data-bs-target = "#helpModal" >
< i class = "bi bi-question-circle" > < / i > Ajuda
< / button >
<!-- Modal de Ajuda -->
< div class = "modal fade" id = "helpModal" tabindex = "-1" aria-labelledby = "helpModalLabel" aria-hidden = "true" >
< div class = "modal-dialog modal-lg modal-dialog-scrollable" >
< div class = "modal-content" >
< div class = "modal-header bg-primary text-white" >
< h5 class = "modal-title" id = "helpModalLabel" > Ajuda - Sistema de Busca de Diários Oficiais< / h5 >
< button type = "button" class = "btn-close btn-close-white" data-bs-dismiss = "modal" aria-label = "Fechar" > < / button >
< / div >
< div class = "modal-body" >
< div class = "mb-4" >
< h6 > Atenção quanto à qualidade dos dados< / h6 >
< p > Alguns documentos podem conter erros de leitura devido à baixa qualidade das imagens dos PDFs originais. Isso pode afetar a precisão da busca, especialmente no modo de < strong > busca exata< / strong > .< / p >
< p > Nesses casos, recomendamos utilizar o modo < strong > qualquer termo< / strong > , que é mais tolerante a pequenas falhas de reconhecimento de texto.< / p >
< / div >
< h5 > Como realizar buscas< / h5 >
< hr >
< div class = "mb-4" >
< h6 > Busca básica< / h6 >
< p > Digite o termo que deseja buscar no campo principal e clique em "Buscar". O sistema irá localizar ocorrências desse termo nos Diários Oficiais.< / p >
< ul >
< li > < strong > Número do Diário:< / strong > Se souber o número específico do diário, digite-o neste campo para filtrar os resultados.< / li >
< li > < strong > Modo de Busca:< / strong >
< ul >
< li > < em > Busca exata< / em > - Encontra apenas documentos que contenham exatamente o termo informado.< / li >
< li > < em > Qualquer termo< / em > - Encontra documentos que contenham qualquer um dos termos informados.< / li >
< / ul >
< / li >
< / ul >
< / div >
< div class = "mb-4" >
< h6 > Ordenação dos resultados< / h6 >
< p > Você pode ordenar os resultados de três formas:< / p >
< ul >
< li > < strong > Relevância:< / strong > Mostra primeiro os documentos mais relevantes para sua busca.< / li >
< li > < strong > Data (Decrescente):< / strong > Mostra os diários mais recentes primeiro.< / li >
< li > < strong > Data (Crescente):< / strong > Mostra os diários mais antigos primeiro.< / li >
< / ul >
< / div >
< div class = "mb-4" >
< h6 > Filtros avançados< / h6 >
< p > Clique em "Mostrar filtros avançados" para acessar opções adicionais:< / p >
< ul >
< li > < strong > Data inicial e Data final:< / strong > Restringe a busca a diários publicados dentro do período informado.< / li >
< li > < strong > Resultados por página:< / strong > Define quantos resultados serão exibidos em cada página.< / li >
< / ul >
< / div >
< div class = "mb-4" >
< h6 > Resultados da busca< / h6 >
< p > Nos resultados, você verá:< / p >
< ul >
< li > Tipo e número do diário, com a data de publicação.< / li >
< li > A página com melhor correspondência aparecerá destacada.< / li >
< li > Para ver mais páginas do mesmo diário, clique no botão de expansão.< / li >
< li > Use o botão "Ver Diário Completo" para abrir o arquivo PDF do diário inteiro.< / li >
< / ul >
< / div >
2025-05-26 14:22:19 +02:00
2025-06-16 13:11:57 -04:00
< div class = "mb-4" >
< h6 > Sugestões de busca< / h6 >
< p > Se o sistema encontrar um termo semelhante ao que você buscou, mostrará uma sugestão que você pode clicar para realizar uma nova busca com o termo sugerido.< / p >
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-primary" data-bs-dismiss = "modal" > Entendi< / button >
< / div >
< / div >
< / div >
< / div >
2025-05-26 14:22:19 +02:00
<!-- Bootstrap Bundle with Popper -->
2025-06-16 13:11:57 -04:00
< script src = "./js/bootstrap.bundle.min.js" > < / script >
2025-05-26 14:22:19 +02:00
<!-- Script da aplicação -->
2025-06-16 13:11:57 -04:00
< script src = "./js/config.js" > < / script >
< script src = "./js/script.js" > < / script >
2025-05-26 14:22:19 +02:00
< / body >
< / html >