Files
2025-06-16 13:11:57 -04:00

395 lines
25 KiB
HTML

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diários Oficiais</title>
<!-- Bootstrap 5 CSS -->
<link href="./css/bootstrap-custom.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="./css/bootstrap-icons.css">
<!-- Alpine.js -->
<script defer src="./js/alpine.min.js"></script>
<!-- Estilos customizados -->
<link rel="stylesheet" href="./css/styles.css">
<link rel="icon" href="./img/favicon.ico" type="image/x-icon">
</head>
<body class="bg-light-gradient">
<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>
<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">
<i class="bi bi-search me-2"></i>Diários Oficiais
</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">&laquo;</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">&raquo;</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">
<h5>Diários Oficiais</h5>
</div>
<div class="col-md-6 text-md-end">
<p class="small mb-0">&copy; 2025 Todos os direitos reservados</p>
</div>
</div>
</div>
</footer>
<!-- 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>
<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>
<!-- Bootstrap Bundle with Popper -->
<script src="./js/bootstrap.bundle.min.js"></script>
<!-- Script da aplicação -->
<script src="./js/config.js"></script>
<script src="./js/script.js"></script>
</body>
</html>