Browse Source

Merge pull request #3 from Move-System/sp_importante_virou_urgente

Sp importante virou urgente
pull/3858/head
Kemuel-sepulvida 4 months ago
committed by GitHub
parent
commit
0f7b071698
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      sapl/context_processors.py
  2. 6
      sapl/materia/forms.py
  3. 8
      sapl/materia/urls.py
  4. 22
      sapl/materia/views_ged.py
  5. 1
      sapl/settings.py
  6. 15
      sapl/templates/index.html
  7. 151
      sapl/templates/materia/ged_historico.html
  8. 221
      sapl/templates/materia/proposicao_form.html
  9. 2
      sapl/templates/navbar.yaml

6
sapl/context_processors.py

@ -84,6 +84,12 @@ def pendencias_assinatura(request):
}
def ged_configurado(request):
"""Injeta flag indicando se o GED (acervo histórico) está configurado."""
from sapl.materia.views_ged import GED_URL
return {'ged_configurado': bool(GED_URL)}
@cached_call("site-title", timeout=60 * 2)
def enable_sapn(request):
verbose_name = _('SGVP') \

6
sapl/materia/forms.py

@ -2003,10 +2003,8 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
label=_('Co-autores'),
required=False,
queryset=Autor.objects.all(),
widget=forms.SelectMultiple(attrs={
'class': 'select2-coautores',
'style': 'width: 100%',
'data-placeholder': _('Selecione os co-autores...')
widget=forms.CheckboxSelectMultiple(attrs={
'class': 'coautores-checkbox',
}),
help_text=_('Selecione os demais autores deste documento. '
'Eles serão adicionados como co-autores ao incorporar a proposição.')

8
sapl/materia/urls.py

@ -1,5 +1,6 @@
from django.conf.urls import include, url
from sapl.materia.views_ged import ged_historico
from sapl.materia.views import (AcompanhamentoConfirmarView,
AcompanhamentoExcluirView,
AcompanhamentoMateriaView, AnexadaCrud,
@ -350,5 +351,10 @@ urlpatterns_sistema = [
url(r'^sistema/materia/config-etiqueta-materia-legislativas/',configEtiquetaMateriaLegislativaCrud, name="configEtiquetaMateriaLegislativaCrud"),
]
urlpatterns_ged = [
url(r'^materia/acervo-historico/$', ged_historico,
name='ged_historico'),
]
urlpatterns = urlpatterns_impressos + urlpatterns_materia + \
urlpatterns_proposicao + urlpatterns_sistema
urlpatterns_proposicao + urlpatterns_sistema + urlpatterns_ged

22
sapl/materia/views_ged.py

@ -0,0 +1,22 @@
"""
View pública para acesso ao acervo histórico via GED (sistema legado).
Issue #1326 / Task #1348 – Adicionar tela do GED dentro do Legislativo.
"""
from decouple import config
from django.shortcuts import render
from django.views.decorators.cache import cache_control
# Sem default: se GED_URL não estiver no .env, retorna string vazia.
GED_URL = config('GED_URL', default='')
@cache_control(public=True, max_age=300)
def ged_historico(request):
"""
Exibe o sistema GED (acervo histórico matérias anteriores a 2026)
incorporado em iframe. Acesso público, sem autenticação.
Se GED_URL não estiver configurado, exibe mensagem informativa.
"""
return render(request, 'materia/ged_historico.html', {
'ged_url': GED_URL or '',
})

1
sapl/settings.py

@ -235,6 +235,7 @@ TEMPLATES = [
'sapl.context_processors.google_recaptcha_configured',
'sapl.context_processors.enable_sapn',
'sapl.context_processors.pendencias_assinatura',
'sapl.context_processors.ged_configurado',
],
'debug': DEBUG
},

15
sapl/templates/index.html

@ -155,6 +155,21 @@
</a>
</div>
</div>
<!-- Acervo Histórico (GED) -->
{% if ged_configurado %}
<div class="homeBlock">
<div class="card-icon">
<i class="fas fa-archive"></i>
</div>
<h3 class="card-title">Acervo Histórico</h3>
<p class="card-description">Matérias anteriores a 2026 (sistema legado)</p>
<div class="card-actions">
<a href="{% url 'sapl.materia:ged_historico' %}" class="btn-action" title="Acessar Acervo Histórico">
<i class="fas fa-folder-open"></i> Acessar
</a>
</div>
</div>
{% endif %}
<!-- Relatórios -->
<div class="homeBlock">

151
sapl/templates/materia/ged_historico.html

@ -0,0 +1,151 @@
{% extends "base.html" %}
{% load i18n staticfiles %}
{% block head_title %}Acervo Histórico – Matérias Anteriores a 2026 – {{ nome_casa }}{% endblock %}
{% block head_content %}
{{ block.super }}
<style>
html, body { height: 100%; overflow: hidden; }
/* Esconde footer nesta página */
#footer, footer.footer { display: none !important; }
/* Remove container padrão do conteúdo */
#content.content.page__row { padding: 0 !important; margin: 0 !important; }
#content .container { max-width: 100% !important; padding: 0 !important; margin: 0 !important; width: 100% !important; }
#ged-wrapper {
display: flex;
flex-direction: column;
height: calc(100vh - 56px);
}
#ged-header {
flex-shrink: 0;
background: linear-gradient(135deg, #1a3a5c 0%, #2e6da4 100%);
color: #fff;
padding: 12px 24px;
display: flex;
align-items: center;
gap: 14px;
box-shadow: 0 2px 6px rgba(0,0,0,.25);
}
#ged-header .ged-icon { font-size: 1.8rem; opacity: .9; }
#ged-header .ged-title { flex: 1; }
#ged-header .ged-title h1 { font-size: 1.05rem; font-weight: 700; margin: 0 0 2px; }
#ged-header .ged-title p { font-size: .75rem; margin: 0; opacity: .85; }
#ged-header .ged-actions a { color: #fff; border-color: rgba(255,255,255,.6); font-size: .8rem; }
#ged-header .ged-actions a:hover { background: rgba(255,255,255,.15); border-color: #fff; }
#ged-frame-container { flex: 1; position: relative; overflow: hidden; }
#ged-loading {
position: absolute; inset: 0;
display: flex; flex-direction: column; align-items: center; justify-content: center;
background: #f8f9fa; z-index: 10; gap: 12px;
color: #555; font-size: .9rem; transition: opacity .4s;
}
#ged-loading.hidden { opacity: 0; pointer-events: none; }
#ged-error {
display: none; flex-direction: column; align-items: center; justify-content: center;
height: 100%; gap: 12px; color: #555; text-align: center; padding: 24px;
}
#ged-error i { font-size: 3rem; color: #c0392b; }
#ged-frame { width: 100%; height: 100%; border: none; display: block; }
</style>
{% endblock %}
{% block content_subnav %}{% endblock %}
{% block footer_container %}{% endblock %}
{% block content_container %}
{% if ged_url %}
<div id="ged-wrapper">
<div id="ged-header">
<div class="ged-icon"><i class="fas fa-archive"></i></div>
<div class="ged-title">
<h1>Acervo Histórico – Matérias Legislativas</h1>
<p>Documentos do sistema anterior (GED) · registros anteriores a 2026</p>
</div>
<div class="ged-actions d-none d-md-flex" style="gap:8px;">
<a href="{{ ged_url }}" target="_blank" rel="noopener noreferrer"
class="btn btn-sm btn-outline-light" title="Abrir em nova aba">
<i class="fas fa-external-link-alt"></i> Abrir em nova aba
</a>
</div>
</div>
<div id="ged-frame-container">
<div id="ged-loading">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Carregando…</span>
</div>
<span>Carregando acervo histórico…</span>
</div>
<div id="ged-error">
<i class="fas fa-exclamation-triangle"></i>
<strong>Não foi possível carregar o acervo histórico.</strong>
<p class="mb-1">O sistema GED pode estar temporariamente indisponível.</p>
<a href="{{ ged_url }}" target="_blank" rel="noopener noreferrer"
class="btn btn-primary btn-sm mt-2">
<i class="fas fa-external-link-alt"></i> Tentar acessar diretamente
</a>
</div>
<iframe
id="ged-frame"
src="{{ ged_url }}"
title="Acervo Histórico – Matérias Legislativas (GED)"
loading="lazy"
allow="fullscreen"
></iframe>
</div>
</div>
<script>
(function () {
var frame = document.getElementById('ged-frame');
var loading = document.getElementById('ged-loading');
var error = document.getElementById('ged-error');
frame.addEventListener('load', function () {
loading.classList.add('hidden');
setTimeout(function () { loading.style.display = 'none'; }, 450);
});
var timeout = setTimeout(function () {
if (!loading.classList.contains('hidden')) {
loading.style.display = 'none';
frame.style.display = 'none';
error.style.display = 'flex';
}
}, 15000);
frame.addEventListener('load', function () { clearTimeout(timeout); });
})();
</script>
{% else %}
<div style="display:flex; flex-direction:column; align-items:center; justify-content:center;
min-height: 60vh; text-align:center; padding: 40px 24px; color:#555;">
<i class="fas fa-archive" style="font-size:4rem; color:#adb5bd; margin-bottom:20px;"></i>
<h2 style="font-size:1.4rem; font-weight:700; color:#343a40; margin-bottom:12px;">
Acervo Histórico não disponível
</h2>
<p style="max-width:480px; font-size:.95rem; line-height:1.6; margin-bottom:24px;">
Esta instalação não possui proposições legadas configuradas.<br>
Caso necessite acessar matérias de sistemas anteriores, entre em contato com o administrador do sistema.
</p>
<a href="/" class="btn btn-outline-secondary">
<i class="fas fa-home"></i> Voltar à página inicial
</a>
</div>
{% endif %}
{% endblock content_container %}

221
sapl/templates/materia/proposicao_form.html

@ -3,26 +3,68 @@
{% load crispy_forms_tags %}
{% block extra_css %}
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style>
/* Select2 co-autores */
.select2-container--default .select2-selection--multiple {
/* =====================================================
Componente de co-autores com busca + checkboxes
===================================================== */
#coautores-search-box {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 6px;
font-size: 0.95em;
margin-bottom: 8px;
box-sizing: border-box;
}
#coautores-search-box:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0,123,255,0.2);
}
#coautores-list-container {
max-height: 220px;
overflow-y: auto;
border: 1px solid #ced4da;
border-radius: 4px;
min-height: 38px;
padding: 2px 6px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #007bff;
border: none;
color: #fff;
border-radius: 3px;
padding: 2px 8px;
border-radius: 6px;
background: #fff;
padding: 6px 0;
}
#coautores-list-container .coautores-item {
display: flex;
align-items: center;
padding: 6px 12px;
cursor: pointer;
transition: background 0.15s;
}
#coautores-list-container .coautores-item:hover {
background: #f0f7ff;
}
#coautores-list-container .coautores-item input[type="checkbox"] {
margin-right: 10px;
width: 16px;
height: 16px;
accent-color: #007bff;
cursor: pointer;
flex-shrink: 0;
}
#coautores-list-container .coautores-item label {
margin: 0;
cursor: pointer;
font-weight: normal;
color: #343a40;
font-size: 0.95em;
}
#coautores-no-results {
padding: 10px 12px;
color: #6c757d;
font-style: italic;
font-size: 0.9em;
display: none;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #fff;
margin-right: 5px;
#coautores-counter {
font-size: 0.85em;
color: #6c757d;
margin-top: 5px;
}
#div_id_coautores {
background: #f8f9fa;
@ -229,19 +271,115 @@
{% endblock %}
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
// =====================================================
// Inicializar Select2 para co-autores
// Componente checkbox + busca para co-autores
// =====================================================
$('.select2-coautores').select2({
placeholder: '{% trans "Selecione os co-autores..." %}',
allowClear: true,
language: 'pt-BR',
width: '100%'
(function() {
var $fieldDiv = $('#div_id_coautores');
// O Django renderiza os checkboxes nativos dentro de um <ul> — vamos substituí-los
var $originalWidget = $fieldDiv.find('ul, .checkbox-select, select');
// Coletar todos os checkboxes originais gerados pelo CheckboxSelectMultiple
var checkboxes = [];
$fieldDiv.find('input[type="checkbox"]').each(function() {
checkboxes.push({
value: $(this).val(),
label: $(this).closest('label').text().trim() ||
$fieldDiv.find('label[for="' + $(this).attr('id') + '"]').text().trim(),
checked: $(this).prop('checked'),
id: $(this).attr('id'),
name: $(this).attr('name')
});
});
if (checkboxes.length === 0) return; // Sem autores cadastrados, não renderizar
// Esconder widget original (manter checkboxes no DOM para submit)
$fieldDiv.find('ul').css('display', 'none');
// Inserir campo de busca e container customizado após a label
var $label = $fieldDiv.find('label').first();
var $helpText = $fieldDiv.find('.form-text, small.text-muted');
var $searchInput = $('<input>', {
type: 'text',
id: 'coautores-search-box',
placeholder: '{% trans "Buscar co-autor por nome..." %}',
autocomplete: 'off'
});
var $listContainer = $('<div>', { id: 'coautores-list-container' });
var $noResults = $('<div>', { id: 'coautores-no-results', text: '{% trans "Nenhum autor encontrado." %}' });
var $counter = $('<div>', { id: 'coautores-counter' });
$listContainer.append($noResults);
// Renderizar itens
function renderItem(cb) {
var $item = $('<div>', { 'class': 'coautores-item' });
var $chk = $('<input>', {
type: 'checkbox',
id: cb.id + '_custom',
value: cb.value,
checked: cb.checked
});
var $lbl = $('<label>', {
'for': cb.id + '_custom',
text: cb.label
});
$item.append($chk).append($lbl);
// Sincronizar com checkbox original (para submit)
$chk.on('change', function() {
$('#' + cb.id).prop('checked', $(this).prop('checked'));
updateCounter();
});
return $item;
}
checkboxes.forEach(function(cb) {
$listContainer.append(renderItem(cb));
});
function updateCounter() {
var total = $listContainer.find('input[type="checkbox"]:checked').length;
if (total === 0) {
$counter.text('');
} else if (total === 1) {
$counter.text('1 {% trans "co-autor selecionado" %}');
} else {
$counter.text(total + ' {% trans "co-autores selecionados" %}');
}
}
// Busca em tempo real
$searchInput.on('input', function() {
var term = $(this).val().toLowerCase().trim();
var visible = 0;
$listContainer.find('.coautores-item').each(function() {
var name = $(this).find('label').text().toLowerCase();
if (!term || name.indexOf(term) !== -1) {
$(this).show();
visible++;
} else {
$(this).hide();
}
});
$noResults.toggle(visible === 0);
});
// Inserir no DOM
$label.after($searchInput, $listContainer, $counter);
if ($helpText.length) {
$counter.after($helpText.detach());
}
updateCounter();
})();
// =====================================================
// Configuração do botão "Novo Tipo"
@ -251,7 +389,6 @@ $(document).ready(function(){
var $tipoSelect = $("select[name=tipo]");
if ($tipoLabel.length > 0 && $tipoSelect.length > 0) {
// Criar header com label e botão
var $header = $('<div class="tipo-header"></div>');
var $btnNovoTipo = $(`
<a href="/sistema/proposicao/tipo/create"
@ -261,8 +398,6 @@ $(document).ready(function(){
<i class="fa fa-plus"></i> Novo Tipo
</a>
`);
// Mover label para o header e adicionar botão
$tipoLabel.wrap($header);
$tipoLabel.after($btnNovoTipo);
}
@ -274,54 +409,37 @@ $(document).ready(function(){
var tipoTextoValue = $("input[name=tipo_texto]:checked").val();
if (tipoTextoValue === 'D') {
// Arquivo Digital: mostrar campo de upload
$("#div_id_texto_original").slideDown(200);
$("#onlyoffice-info").slideUp(200);
} else if (tipoTextoValue === 'T') {
// Texto Articulado: esconder ambos
$("#div_id_texto_original").slideUp(200);
$("#onlyoffice-info").slideUp(200);
} else if (tipoTextoValue === 'O') {
// OnlyOffice: mostrar mensagem informativa
$("#div_id_texto_original").slideUp(200);
$("#onlyoffice-info").slideDown(200);
}
// Atualizar visual dos labels
$("#div_id_tipo_texto .form-check label, #div_id_tipo_texto .form-check-inline").removeClass('checked');
$("input[name=tipo_texto]:checked").closest('label').addClass('checked');
}
// Event listener para mudança de tipo de texto
$("input[name=tipo_texto]").change(function() {
updateTipoTextoDisplay();
});
// =====================================================
// Mostrar opções de tipo de texto
// =====================================================
function showTipoTextoOptions() {
var $tipoTextoContainer = $("input[name=tipo_texto]").closest('.form-group').parent();
$tipoTextoContainer.removeClass('hidden');
$("input[name=tipo_texto]").prop('disabled', false);
$("input[name=tipo_texto]").closest('label').removeClass('disabled');
// Selecionar primeira opção se nenhuma estiver selecionada
if ($("input[name=tipo_texto]:checked").length === 0) {
$("input[name=tipo_texto]").first().prop('checked', true);
}
updateTipoTextoDisplay();
}
// Mostrar opções ao carregar a página
showTipoTextoOptions();
// Atualizar quando tipo de proposição mudar
$("select[name=tipo]").change(function() {
showTipoTextoOptions();
});
$("select[name=tipo]").change(function() { showTipoTextoOptions(); });
// =====================================================
// Busca de matéria legislativa para vinculação
@ -332,15 +450,11 @@ $(document).ready(function(){
'ano': $("input[name=ano_materia]").val(),
'numero': $("input[name=numero_materia]").val(),
};
// Validar se todos os campos estão preenchidos
if (!formData.tipo || !formData.ano || !formData.numero) {
$(".ementa_materia").html('').addClass('hidden');
return;
}
var url = '{% url "sapl.api:materialegislativa-list" %}';
$.get(url, formData).done(function(data) {
if (data.pagination.total_entries === 1) {
$(".ementa_materia")
@ -360,24 +474,15 @@ $(document).ready(function(){
});
}
// Event listeners para busca de matéria
$("select[name=tipo_materia], input[name=numero_materia], input[name=ano_materia]")
.on('change keyup', function() {
buscarMateria();
});
.on('change keyup', function() { buscarMateria(); });
// =====================================================
// Inicialização
// =====================================================
// Disparar eventos iniciais
$("select[name=tipo]").trigger('change');
buscarMateria();
// Garantir que o onlyoffice-info comece escondido
$("#onlyoffice-info").hide();
// Atualizar display inicial
setTimeout(updateTipoTextoDisplay, 100);
});

2
sapl/templates/navbar.yaml

@ -49,6 +49,8 @@
check_permission: materia.can_access_impressos {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%}
- title: {% trans 'Matérias Legislativas' %}
url: sapl.materia:pesquisar_materia
- title: {% trans 'Acervo Histórico (antes de 2026)' %}
url: sapl.materia:ged_historico
- title: {% trans 'Matérias Pendentes de Assinatura' %}
url: sapl.materia:materias_pendentes_assinatura
- title: {% trans 'Pautas das Sessões' %}

Loading…
Cancel
Save