304 lines
21 KiB
HTML
304 lines
21 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>Sistema de Busca de Diários Oficiais</title>
|
||
|
|
<!-- Bootstrap 5 CSS -->
|
||
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
|
|
<!-- Bootstrap Icons -->
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||
|
|
<!-- Alpine.js -->
|
||
|
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.1/dist/cdn.min.js"></script>
|
||
|
|
<!-- Estilos customizados -->
|
||
|
|
<link rel="stylesheet" href="css/styles.css">
|
||
|
|
</head>
|
||
|
|
<body class="bg-light-gradient">
|
||
|
|
<div class="container py-5" x-data="searchApp">
|
||
|
|
<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>Sistema de Busca de 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">«</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">
|
||
|
|
<h5>Sistema de Busca de Diários Oficiais</h5>
|
||
|
|
<p class="small">Uma ferramenta avançada para pesquisa em diários oficiais.</p>
|
||
|
|
</div>
|
||
|
|
<div class="col-md-6 text-md-end">
|
||
|
|
<p class="small mb-0">© 2025 Todos os direitos reservados</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</footer>
|
||
|
|
|
||
|
|
<!-- Bootstrap Bundle with Popper -->
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
|
|
<!-- Script da aplicação -->
|
||
|
|
<script src="js/config.js"></script>
|
||
|
|
<script src="js/script.js"></script>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
|