mirror of https://github.com/interlegis/sapl.git
Browse Source
Adiciona pagina onde o Presidente da Mesa Diretora pode visualizar e assinar em lote, com um unico certificado A1, TODOS os documentos acessorios do tipo "Despacho" ainda pendentes de assinatura digital (filtro frouxo: tipo__descricao__icontains "despacho"). Reuso integral do backend ja existente: - docacessorio_assinar_lote() em views_assinatura.py ja aceita PKs de documentos de multiplas materias num unico POST. - Modal de 4 passos (selecao -> certificado -> previa -> progresso -> resumo) extraido de documentoacessorio_list.html para partial reusavel e incluido nos dois templates (refactor mecanico, HTML/JS byte-a-byte identico). Acesso: restrito ao grupo SGVP_GROUP_PRESIDENTE_MESA (ou superuser para depuracao); demais usuarios recebem 403. Atalhos pra tela aparecem so quando usuario esta no grupo: - "Pesquisar Materia Legislativa" (botao na linha de filtro de assinatura) - "Materias Pendentes de Assinatura" (botao no bloco de acoes) Arquivos: - sapl/materia/views.py: ListView DespachosPendentesLoteView nova + flag is_presidente_mesa nos contextos das views existentes. - sapl/materia/urls.py: rota /materia/despachos-pendentes-lote. - sapl/templates/materia/assinatura_doc_lote_modal.html (novo): partial com modal completo. - sapl/templates/materia/despachos_pendentes_lote_list.html (novo): tela do presidente. - sapl/templates/materia/documentoacessorio_list.html: substitui modal embedded por include do partial. - sapl/templates/materia/materialegislativa_filter.html: atalho. - sapl/templates/materia/materias_pendentes_assinatura_list.html: atalho.pull/3858/head
7 changed files with 668 additions and 404 deletions
@ -0,0 +1,420 @@ |
|||
{% comment %} |
|||
Partial reusavel: Modal de Assinatura Digital em Lote de Documentos |
|||
Acessorios. Espera no contexto a variavel `docs_pendentes_lote` |
|||
(lista de dicts com chaves `id` e `descricao`). O caller deve |
|||
envolver com if docs_pendentes_lote include endif e renderizar em |
|||
algum lugar um botao com id="btn-assinar-doc-lote" para disparar o |
|||
modal. |
|||
|
|||
Backend: POST para sapl.materia:docacessorio_assinar_lote |
|||
(views_assinatura.py::docacessorio_assinar_lote). |
|||
|
|||
Usado por: |
|||
- sapl/templates/materia/documentoacessorio_list.html |
|||
(assinar docs acessorios pendentes de UMA materia) |
|||
- sapl/templates/materia/despachos_pendentes_lote_list.html |
|||
(assinar despachos pendentes de TODAS as materias - Presidente da Mesa) |
|||
{% endcomment %} |
|||
<div class="modal fade" id="assinaturaDocLoteModal" tabindex="-1" role="dialog" aria-labelledby="assinaturaDocLoteModalLabel" aria-hidden="true"> |
|||
<div class="modal-dialog modal-lg" role="document"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header bg-warning"> |
|||
<h5 class="modal-title" id="assinaturaDocLoteModalLabel"> |
|||
<i class="fa fa-certificate"></i> |
|||
Assinar Documentos Acessórios em Lote — |
|||
<strong id="doc-lote-titulo-total">{{ docs_pendentes_lote|length }}</strong> pendente(s) |
|||
</h5> |
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Fechar"> |
|||
<span aria-hidden="true">×</span> |
|||
</button> |
|||
</div> |
|||
|
|||
<div class="modal-body"> |
|||
|
|||
{# Passo 0: seleção #} |
|||
<div id="doc-lote-selecao-step"> |
|||
<div class="d-flex justify-content-between align-items-center mb-2"> |
|||
<span class="font-weight-bold"> |
|||
<i class="fa fa-list-ul"></i> Selecione os documentos a assinar |
|||
</span> |
|||
<div> |
|||
<button type="button" class="btn btn-sm btn-outline-secondary mr-1" id="doc-lote-btn-todas"> |
|||
<i class="fa fa-check-square-o"></i> Todos |
|||
</button> |
|||
<button type="button" class="btn btn-sm btn-outline-secondary" id="doc-lote-btn-nenhuma"> |
|||
<i class="fa fa-square-o"></i> Nenhum |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<div id="doc-lote-lista-checkboxes" style="max-height:320px;overflow-y:auto;border:1px solid #dee2e6;border-radius:4px;padding:8px;"> |
|||
{% for d in docs_pendentes_lote %} |
|||
<div class="form-check py-1" style="border-bottom:1px solid #f0f0f0;"> |
|||
<input class="form-check-input doc-lote-chk" type="checkbox" |
|||
id="doc-lote-chk-{{ d.id }}" value="{{ d.id }}" checked> |
|||
<label class="form-check-label w-100" for="doc-lote-chk-{{ d.id }}" style="cursor:pointer;"> |
|||
{{ d.descricao }} |
|||
</label> |
|||
</div> |
|||
{% endfor %} |
|||
</div> |
|||
<p class="text-muted small mt-2 mb-0"> |
|||
<span id="doc-lote-selecao-contagem">{{ docs_pendentes_lote|length }}</span> documento(s) selecionado(s) |
|||
</p> |
|||
</div> |
|||
|
|||
{# Passo 1: certificado #} |
|||
<div id="doc-lote-form-step" style="display:none;"> |
|||
<div class="alert alert-warning"> |
|||
<i class="fa fa-exclamation-triangle"></i> |
|||
<strong>Atenção:</strong> Esta operação assinará digitalmente |
|||
<strong><span id="doc-lote-alerta-total">{{ docs_pendentes_lote|length }}</span> documento(s)</strong> |
|||
com validade jurídica nos termos da MP 2.200-2/2001. |
|||
</div> |
|||
<form id="form-assinatura-doc-lote" enctype="multipart/form-data"> |
|||
{% csrf_token %} |
|||
<div class="form-group"> |
|||
<label for="doc-lote-certificado"> |
|||
<i class="fa fa-file"></i> Certificado Digital (.pfx / .p12) |
|||
</label> |
|||
<div class="custom-file"> |
|||
<input type="file" class="custom-file-input" id="doc-lote-certificado" |
|||
name="certificado" accept=".pfx,.p12" required> |
|||
<label class="custom-file-label" for="doc-lote-certificado"> |
|||
Selecione o arquivo do certificado... |
|||
</label> |
|||
</div> |
|||
</div> |
|||
<div class="form-group"> |
|||
<label for="doc-lote-senha"> |
|||
<i class="fa fa-lock"></i> Senha do Certificado |
|||
</label> |
|||
<input type="password" class="form-control" id="doc-lote-senha" |
|||
name="senha" placeholder="Digite a senha do certificado" required> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
|
|||
{# Passo 1.5: prévia #} |
|||
<div id="doc-lote-previa-step" style="display:none;"> |
|||
<div class="alert alert-info mb-3"> |
|||
<i class="fa fa-eye"></i> |
|||
<strong>Revise os documentos antes de assinar.</strong> |
|||
Clique em cada item para visualizar o PDF em nova aba. |
|||
</div> |
|||
<div id="doc-lote-previa-lista" style="max-height:300px;overflow-y:auto;border:1px solid #dee2e6;border-radius:4px;padding:8px;" class="mb-3"></div> |
|||
<div class="form-check mt-3"> |
|||
<input class="form-check-input" type="checkbox" id="doc-lote-previa-confirmacao"> |
|||
<label class="form-check-label font-weight-bold text-danger" for="doc-lote-previa-confirmacao"> |
|||
<i class="fa fa-check-square-o"></i> |
|||
Confirmo que revisei os documentos e que estão corretos para assinatura. |
|||
</label> |
|||
</div> |
|||
</div> |
|||
|
|||
{# Passo 2: progresso #} |
|||
<div id="doc-lote-progresso-step" style="display:none;"> |
|||
<h6 class="mb-3"> |
|||
<i class="fa fa-spinner fa-spin text-primary"></i> |
|||
Processando assinaturas… |
|||
</h6> |
|||
<div class="progress mb-3" style="height:22px;"> |
|||
<div id="doc-lote-progress-bar" |
|||
class="progress-bar progress-bar-striped progress-bar-animated bg-warning" |
|||
role="progressbar" style="width:0%">0%</div> |
|||
</div> |
|||
<p class="text-muted small" id="doc-lote-status-texto">Iniciando…</p> |
|||
</div> |
|||
|
|||
{# Passo 3: resumo #} |
|||
<div id="doc-lote-resumo-step" style="display:none;"> |
|||
<div id="doc-lote-resumo-alerta"></div> |
|||
<div id="doc-lote-resumo-lista" style="max-height:300px;overflow-y:auto;"></div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-secondary" data-dismiss="modal" id="doc-lote-btn-fechar"> |
|||
<i class="fa fa-times"></i> Fechar |
|||
</button> |
|||
<button type="button" class="btn btn-warning" id="doc-lote-btn-proximo"> |
|||
<i class="fa fa-arrow-right"></i> |
|||
Continuar — <span id="doc-lote-btn-proximo-contagem">{{ docs_pendentes_lote|length }}</span> Doc(s) |
|||
</button> |
|||
<button type="button" class="btn btn-outline-secondary" id="doc-lote-btn-voltar" style="display:none;"> |
|||
<i class="fa fa-arrow-left"></i> Voltar |
|||
</button> |
|||
<button type="button" class="btn btn-info" id="doc-lote-btn-previa" style="display:none;"> |
|||
<i class="fa fa-eye"></i> Visualizar Documentos |
|||
</button> |
|||
<button type="button" class="btn btn-outline-secondary" id="doc-lote-btn-voltar-form" style="display:none;"> |
|||
<i class="fa fa-arrow-left"></i> Voltar |
|||
</button> |
|||
<button type="button" class="btn btn-warning" id="doc-lote-btn-assinar" style="display:none;" disabled> |
|||
<i class="fa fa-certificate"></i> |
|||
Assinar <span id="doc-lote-btn-assinar-contagem">{{ docs_pendentes_lote|length }}</span> Doc(s) |
|||
</button> |
|||
<button type="button" class="btn btn-success" id="doc-lote-btn-recarregar" style="display:none;" |
|||
onclick="location.reload()"> |
|||
<i class="fa fa-refresh"></i> Atualizar página |
|||
</button> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script> |
|||
(function () { |
|||
var DOCS_LOTE = {{ docs_pendentes_lote|safe }}; |
|||
var URL_LOTE = "{% url 'sapl.materia:docacessorio_assinar_lote' %}"; |
|||
|
|||
function getCSRF() { |
|||
var el = document.querySelector('#form-assinatura-doc-lote [name=csrfmiddlewaretoken]'); |
|||
if (el) return el.value; |
|||
var match = document.cookie.match(/csrftoken=([^;]+)/); |
|||
return match ? match[1] : ''; |
|||
} |
|||
|
|||
function getIdsSelecionados() { |
|||
return Array.from(document.querySelectorAll('.doc-lote-chk:checked')) |
|||
.map(function(c){ return parseInt(c.value, 10); }); |
|||
} |
|||
|
|||
function atualizarContagem() { |
|||
var n = getIdsSelecionados().length; |
|||
document.getElementById('doc-lote-selecao-contagem').textContent = n; |
|||
document.getElementById('doc-lote-btn-proximo-contagem').textContent = n; |
|||
document.getElementById('doc-lote-btn-assinar-contagem').textContent = n; |
|||
document.getElementById('doc-lote-alerta-total').textContent = n; |
|||
document.getElementById('doc-lote-titulo-total').textContent = n; |
|||
document.getElementById('doc-lote-btn-proximo').disabled = (n === 0); |
|||
} |
|||
|
|||
document.getElementById('doc-lote-lista-checkboxes').addEventListener('change', function(e){ |
|||
if (e.target && e.target.classList.contains('doc-lote-chk')) atualizarContagem(); |
|||
}); |
|||
|
|||
document.getElementById('doc-lote-btn-todas').addEventListener('click', function(){ |
|||
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = true; }); |
|||
atualizarContagem(); |
|||
}); |
|||
document.getElementById('doc-lote-btn-nenhuma').addEventListener('click', function(){ |
|||
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = false; }); |
|||
atualizarContagem(); |
|||
}); |
|||
|
|||
document.getElementById('doc-lote-certificado').addEventListener('change', function(){ |
|||
var label = this.nextElementSibling; |
|||
label.textContent = this.files[0] ? this.files[0].name : 'Selecione o arquivo do certificado...'; |
|||
}); |
|||
|
|||
function irParaSelecao() { |
|||
document.getElementById('doc-lote-selecao-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-form-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-previa-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-resumo-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-proximo').style.display = 'inline-block'; |
|||
document.getElementById('doc-lote-btn-voltar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-previa').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none'; |
|||
document.getElementById('doc-lote-btn-assinar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-recarregar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-fechar').disabled = false; |
|||
} |
|||
|
|||
function irParaForm() { |
|||
document.getElementById('doc-lote-selecao-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-form-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-previa-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-resumo-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-proximo').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-voltar').style.display = 'inline-block'; |
|||
document.getElementById('doc-lote-btn-previa').style.display = 'inline-block'; |
|||
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none'; |
|||
document.getElementById('doc-lote-btn-assinar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-recarregar').style.display = 'none'; |
|||
} |
|||
|
|||
function irParaPrevia() { |
|||
var certFile = document.getElementById('doc-lote-certificado').files[0]; |
|||
var senha = document.getElementById('doc-lote-senha').value; |
|||
if (!certFile) { alert('Selecione o arquivo do certificado.'); return; } |
|||
if (!senha) { alert('Digite a senha do certificado.'); return; } |
|||
|
|||
var idsSel = getIdsSelecionados(); |
|||
var html = ''; |
|||
DOCS_LOTE.filter(function(d){ return idsSel.indexOf(d.id) !== -1; }) |
|||
.forEach(function(d) { |
|||
html += '<div class="d-flex align-items-center justify-content-between py-2" ' + |
|||
'style="border-bottom:1px solid #f0f0f0;">' + |
|||
'<span><i class="fa fa-file-pdf-o text-danger mr-1"></i>' + |
|||
'<strong>' + escHtml(d.descricao) + '</strong></span>' + |
|||
'<a href="/materia/documentoacessorio/' + d.id + '/pdf-previa" target="_blank" ' + |
|||
'class="btn btn-sm btn-outline-primary ml-2" title="Visualizar PDF">' + |
|||
'<i class="fa fa-eye"></i> Visualizar</a>' + |
|||
'</div>'; |
|||
}); |
|||
document.getElementById('doc-lote-previa-lista').innerHTML = html || '<p class="text-muted">Nenhum documento selecionado.</p>'; |
|||
|
|||
var chk = document.getElementById('doc-lote-previa-confirmacao'); |
|||
chk.checked = false; |
|||
document.getElementById('doc-lote-btn-assinar').disabled = true; |
|||
|
|||
document.getElementById('doc-lote-selecao-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-form-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-previa-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-resumo-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-proximo').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-voltar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-previa').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-voltar-form').style.display= 'inline-block'; |
|||
document.getElementById('doc-lote-btn-assinar').style.display = 'inline-block'; |
|||
document.getElementById('doc-lote-btn-recarregar').style.display = 'none'; |
|||
} |
|||
|
|||
document.getElementById('btn-assinar-doc-lote').addEventListener('click', function(){ |
|||
document.querySelectorAll('.doc-lote-chk').forEach(function(c){ c.checked = true; }); |
|||
document.getElementById('form-assinatura-doc-lote').reset(); |
|||
document.querySelector('#form-assinatura-doc-lote .custom-file-label').textContent = |
|||
'Selecione o arquivo do certificado...'; |
|||
atualizarContagem(); |
|||
irParaSelecao(); |
|||
$('#assinaturaDocLoteModal').modal('show'); |
|||
}); |
|||
|
|||
document.getElementById('doc-lote-btn-proximo').addEventListener('click', function(){ |
|||
if (getIdsSelecionados().length === 0) { alert('Selecione ao menos um documento.'); return; } |
|||
irParaForm(); |
|||
}); |
|||
|
|||
document.getElementById('doc-lote-btn-voltar').addEventListener('click', irParaSelecao); |
|||
document.getElementById('doc-lote-btn-previa').addEventListener('click', irParaPrevia); |
|||
document.getElementById('doc-lote-btn-voltar-form').addEventListener('click', irParaForm); |
|||
|
|||
document.getElementById('doc-lote-previa-confirmacao').addEventListener('change', function(){ |
|||
document.getElementById('doc-lote-btn-assinar').disabled = !this.checked; |
|||
}); |
|||
|
|||
document.getElementById('doc-lote-btn-assinar').addEventListener('click', function(){ |
|||
var certFile = document.getElementById('doc-lote-certificado').files[0]; |
|||
var senha = document.getElementById('doc-lote-senha').value; |
|||
var idsSel = getIdsSelecionados(); |
|||
|
|||
if (!certFile) { alert('Selecione o arquivo do certificado.'); return; } |
|||
if (!senha) { alert('Digite a senha do certificado.'); return; } |
|||
if (!idsSel.length) { alert('Nenhum documento selecionado.'); return; } |
|||
|
|||
var fd = new FormData(); |
|||
fd.append('certificado', certFile); |
|||
fd.append('senha', senha); |
|||
fd.append('ids', JSON.stringify(idsSel)); |
|||
|
|||
document.getElementById('doc-lote-previa-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-btn-voltar-form').style.display= 'none'; |
|||
document.getElementById('doc-lote-btn-assinar').style.display = 'none'; |
|||
document.getElementById('doc-lote-btn-fechar').disabled = true; |
|||
|
|||
// Animação de progresso proporcional ao número de documentos |
|||
var nDocs = idsSel.length; |
|||
var fases = [ |
|||
{ ate: 10, label: 'Enviando certificado e iniciando assinatura…', ms: 700 }, |
|||
{ ate: 25, label: 'Validando certificado e gerando PDFs…', ms: Math.min(600, 200 + nDocs * 30) }, |
|||
{ ate: 50, label: 'Preparando ' + nDocs + ' documento(s) para assinatura…', ms: Math.min(800, 200 + nDocs * 50) }, |
|||
{ ate: 70, label: 'Enviando ao microserviço de assinatura…', ms: Math.min(1000, 300 + nDocs * 60) }, |
|||
{ ate: 85, label: 'Aguardando resposta do servidor…', ms: Math.min(800, 300 + nDocs * 40) }, |
|||
{ ate: 93, label: 'Salvando documentos assinados…', ms: 500 }, |
|||
{ ate: 97, label: 'Finalizando…', ms: 300 }, |
|||
]; |
|||
var faseIdx = 0, progrAtual = 0, progrTimer = null; |
|||
function avancarProgresso() { |
|||
if (faseIdx >= fases.length) return; |
|||
var fase = fases[faseIdx]; |
|||
if (progrAtual < fase.ate) { |
|||
progrAtual = Math.min(progrAtual + 1, fase.ate); |
|||
setProgresso(progrAtual, fase.label); |
|||
} else { faseIdx++; } |
|||
progrTimer = setTimeout(avancarProgresso, fases[Math.min(faseIdx, fases.length-1)].ms / (fase.ate - (faseIdx > 0 ? fases[faseIdx-1].ate : 0))); |
|||
} |
|||
progrTimer = setTimeout(avancarProgresso, 400); |
|||
|
|||
fetch(URL_LOTE, { |
|||
method: 'POST', |
|||
body: fd, |
|||
headers: { 'X-CSRFToken': getCSRF() } |
|||
}) |
|||
.then(function(r){ |
|||
clearTimeout(progrTimer); |
|||
setProgresso(98, 'Processando resposta…'); |
|||
if (!r.ok) return r.json().then(function(d){ throw new Error(d.error || ('HTTP ' + r.status)); }); |
|||
return r.json(); |
|||
}) |
|||
.then(function(data){ |
|||
clearTimeout(progrTimer); |
|||
setProgresso(100, 'Concluído.'); |
|||
document.getElementById('doc-lote-btn-fechar').disabled = false; |
|||
mostrarResumo(data); |
|||
}) |
|||
.catch(function(err){ |
|||
clearTimeout(progrTimer); |
|||
document.getElementById('doc-lote-btn-fechar').disabled = false; |
|||
setProgresso(100, 'Erro.'); |
|||
mostrarErroFatal(err.message || String(err)); |
|||
}); |
|||
}); |
|||
|
|||
function setProgresso(pct, texto) { |
|||
var bar = document.getElementById('doc-lote-progress-bar'); |
|||
bar.style.width = pct + '%'; |
|||
bar.textContent = pct + '%'; |
|||
document.getElementById('doc-lote-status-texto').textContent = texto; |
|||
} |
|||
|
|||
function mostrarResumo(data) { |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-resumo-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-btn-recarregar').style.display = 'inline-block'; |
|||
|
|||
var temErro = data.erros > 0; |
|||
var alertaCls = data.sucesso > 0 ? (temErro ? 'alert-warning' : 'alert-success') : 'alert-danger'; |
|||
var icone = data.sucesso > 0 ? (temErro ? 'exclamation-triangle' : 'check-circle') : 'times-circle'; |
|||
|
|||
document.getElementById('doc-lote-resumo-alerta').innerHTML = |
|||
'<div class="alert ' + alertaCls + '">' + |
|||
'<i class="fa fa-' + icone + '"></i> ' + |
|||
'<strong>' + data.sucesso + ' assinado(s)</strong> com sucesso' + |
|||
(temErro ? ', <strong>' + data.erros + '</strong> com erro(s).' : '.') + |
|||
' Total: ' + data.total + ' documento(s).</div>'; |
|||
|
|||
var html = '<ul class="list-group">'; |
|||
(data.resultados || []).forEach(function(r){ |
|||
var cls = r.success ? 'list-group-item-success' : 'list-group-item-danger'; |
|||
var icon = r.success ? 'check text-success' : 'times text-danger'; |
|||
html += '<li class="list-group-item list-group-item-sm ' + cls + '">' + |
|||
'<i class="fa fa-' + icon + ' mr-1"></i>' + |
|||
'<strong>' + escHtml(r.descricao) + '</strong>' + |
|||
(r.error ? ' — <small class="text-muted">' + escHtml(r.error) + '</small>' : '') + |
|||
'</li>'; |
|||
}); |
|||
html += '</ul>'; |
|||
document.getElementById('doc-lote-resumo-lista').innerHTML = html; |
|||
} |
|||
|
|||
function mostrarErroFatal(msg) { |
|||
document.getElementById('doc-lote-progresso-step').style.display = 'none'; |
|||
document.getElementById('doc-lote-resumo-step').style.display = 'block'; |
|||
document.getElementById('doc-lote-resumo-alerta').innerHTML = |
|||
'<div class="alert alert-danger"><i class="fa fa-times-circle"></i> ' + |
|||
'<strong>Erro:</strong> ' + escHtml(msg) + '</div>'; |
|||
document.getElementById('doc-lote-resumo-lista').innerHTML = ''; |
|||
} |
|||
|
|||
function escHtml(s) { |
|||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); |
|||
} |
|||
})(); |
|||
</script> |
|||
@ -0,0 +1,136 @@ |
|||
{% extends "crud/detail.html" %} |
|||
{% load i18n %} |
|||
{% load static %} |
|||
|
|||
{% block actions %} |
|||
<div class="actions btn-group float-right pb-4" role="group"> |
|||
<a href="{% url 'sapl.materia:materias_pendentes_assinatura' %}" class="btn btn-outline-secondary"> |
|||
<i class="fas fa-arrow-left"></i> {% trans "Voltar para Minhas Matérias" %} |
|||
</a> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
{% block detail_content %} |
|||
|
|||
<div class="d-flex align-items-center justify-content-between mb-3 flex-wrap" style="gap:.75rem;"> |
|||
<div> |
|||
<h4 class="mb-0"> |
|||
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:1rem;"> |
|||
<i class="fas fa-stamp mr-1"></i> {% trans "Despachos Pendentes de Assinatura" %} |
|||
</span> |
|||
</h4> |
|||
<small class="text-muted mt-1 d-block"> |
|||
<i class="fas fa-user-tie mr-1"></i> |
|||
{% trans "Acesso exclusivo do" %} <strong>{% trans "Presidente da Mesa Diretora" %}</strong> |
|||
</small> |
|||
</div> |
|||
<div> |
|||
{% if total == 0 %} |
|||
<span class="badge badge-success px-3 py-2" style="font-size:.95rem;"> |
|||
<i class="fas fa-check-circle mr-1"></i> {% trans "Nenhum despacho pendente" %} |
|||
</span> |
|||
{% elif total == 1 %} |
|||
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:.95rem;"> |
|||
1 {% trans "despacho pendente" %} |
|||
</span> |
|||
{% else %} |
|||
<span class="badge badge-warning text-dark px-3 py-2" style="font-size:.95rem;"> |
|||
{{ total }} {% trans "despachos pendentes" %} |
|||
</span> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
|
|||
{% if not object_list %} |
|||
<div class="alert alert-success d-flex align-items-center" role="alert"> |
|||
<i class="fas fa-check-circle fa-2x mr-3"></i> |
|||
<div> |
|||
<strong>{% trans "Tudo em dia!" %}</strong><br> |
|||
{% trans "Não há despachos pendentes de assinatura digital no sistema." %} |
|||
</div> |
|||
</div> |
|||
{% else %} |
|||
|
|||
<div class="alert alert-warning d-flex justify-content-between align-items-center mb-3 flex-wrap" |
|||
role="alert" style="border-left: 5px solid #f0ad4e; gap:1rem;"> |
|||
<div> |
|||
<i class="fas fa-exclamation-triangle fa-lg mr-2"></i> |
|||
{% trans "Os despachos abaixo aguardam assinatura digital. Você pode assiná-los todos em lote com seu certificado digital." %} |
|||
</div> |
|||
<button type="button" id="btn-assinar-doc-lote" |
|||
class="btn btn-warning" |
|||
title="{% trans 'Assinar digitalmente todos os despachos selecionados em lote' %}"> |
|||
<i class="fa fa-certificate"></i> |
|||
{% trans "Assinar Despachos em Lote" %} |
|||
<span class="badge badge-light text-dark ml-1">{{ docs_pendentes_lote|length }}</span> |
|||
</button> |
|||
</div> |
|||
|
|||
<table class="table table-hover table-bordered"> |
|||
<thead class="thead-light"> |
|||
<tr> |
|||
<th style="width:170px;">{% trans "Matéria" %}</th> |
|||
<th>{% trans "Nome do Despacho" %}</th> |
|||
<th style="width:140px;">{% trans "Tipo" %}</th> |
|||
<th style="width:140px;">{% trans "Autor" %}</th> |
|||
<th style="width:110px;" class="text-center">{% trans "Data" %}</th> |
|||
<th style="width:90px;" class="text-center">{% trans "Ações" %}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for d in object_list %} |
|||
<tr> |
|||
<td> |
|||
<a href="{% url 'sapl.materia:materialegislativa_detail' d.materia.pk %}" |
|||
class="font-weight-bold" |
|||
title="{% trans 'Abrir matéria' %}"> |
|||
{{ d.materia.tipo.sigla }} {{ d.materia.numero }}/{{ d.materia.ano }} |
|||
</a> |
|||
</td> |
|||
<td> |
|||
<a href="{% url 'sapl.materia:documentoacessorio_detail' d.pk %}" |
|||
title="{% trans 'Abrir documento' %}"> |
|||
{{ d.nome }} |
|||
</a> |
|||
</td> |
|||
<td> |
|||
<small class="text-muted">{{ d.tipo }}</small> |
|||
</td> |
|||
<td> |
|||
{% if d.autor %} |
|||
{{ d.autor }} |
|||
{% else %} |
|||
<span class="text-muted">—</span> |
|||
{% endif %} |
|||
</td> |
|||
<td class="text-center"> |
|||
{% if d.data %} |
|||
{{ d.data|date:"d/m/Y" }} |
|||
{% else %} |
|||
<span class="text-muted">—</span> |
|||
{% endif %} |
|||
</td> |
|||
<td class="text-center"> |
|||
{% if d.arquivo %} |
|||
<a href="{{ d.arquivo.url }}" target="_blank" |
|||
class="btn btn-sm btn-outline-primary" |
|||
title="{% trans 'Baixar arquivo original' %}"> |
|||
<i class="fas fa-download"></i> |
|||
</a> |
|||
{% else %} |
|||
<span class="text-muted">—</span> |
|||
{% endif %} |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</tbody> |
|||
</table> |
|||
|
|||
{% include "paginacao.html" %} |
|||
|
|||
{# Modal de assinatura em lote (partial reusado de documentoacessorio_list.html) #} |
|||
{% include "materia/assinatura_doc_lote_modal.html" %} |
|||
|
|||
{% endif %} |
|||
|
|||
{% endblock detail_content %} |
|||
Loading…
Reference in new issue