feat: botao de gerar chave de reativacao por linha na lista de clientes
This commit is contained in:
@@ -242,6 +242,10 @@ a.botao:hover { border-color: var(--cor-acento); color: var(--cor-acento); }
|
||||
.paginacao-atual { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; height: 32px; padding: 0 6px; border-radius: 6px; background: var(--cor-acento); color: var(--cor-acento-texto); font-weight: 600; }
|
||||
.paginacao-reticencias { padding: 0 4px; color: var(--cor-texto-secundario); }
|
||||
|
||||
.chave-feedback { margin-left: 6px; font-size: 0.85rem; color: var(--cor-sucesso); }
|
||||
.chave-feedback-erro { color: var(--cor-perigo); }
|
||||
.botao-gerar-chave { margin-left: 6px; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after { transition: none !important; }
|
||||
}
|
||||
|
||||
@@ -39,7 +39,11 @@
|
||||
<td>{{ c.RAZAO }}</td>
|
||||
<td>{{ c.FANTASIA }}</td>
|
||||
<td>{{ c.CNPJ }}</td>
|
||||
<td><a class="botao" href="/clientes/{{ c.ID }}">Abrir</a></td>
|
||||
<td>
|
||||
<a class="botao" href="/clientes/{{ c.ID }}">Abrir</a>
|
||||
<button type="button" class="botao botao-gerar-chave" data-codigo="{{ c.CODIGO }}" onclick="gerarChaveReativacao(this)">Gerar chave</button>
|
||||
<span class="chave-feedback" aria-live="polite"></span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
@@ -80,4 +84,68 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Gera a chave de reativação do mês vigente pro cliente da linha
|
||||
// clicada, reaproveitando o endpoint já usado pelo gerador manual
|
||||
// (/gerador-chave/gerar) — o algoritmo de cifra roda só no backend
|
||||
// (app/gerador_chave.py), esta página só manda código+mês/ano e
|
||||
// copia o resultado.
|
||||
const CSRF_TOKEN = "{{ csrf_token }}";
|
||||
|
||||
function _mesAnoAtual() {
|
||||
const hoje = new Date();
|
||||
return `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
async function gerarChaveReativacao(botao) {
|
||||
const codigo = botao.dataset.codigo;
|
||||
const feedback = botao.parentElement.querySelector('.chave-feedback');
|
||||
botao.disabled = true;
|
||||
feedback.textContent = '';
|
||||
feedback.classList.remove('chave-feedback-erro');
|
||||
|
||||
const corpo = new URLSearchParams({
|
||||
csrf_token: CSRF_TOKEN,
|
||||
tipo: 'reativacao',
|
||||
codigo: codigo,
|
||||
referencia: _mesAnoAtual(),
|
||||
});
|
||||
|
||||
let resposta, dados;
|
||||
try {
|
||||
resposta = await fetch('/gerador-chave/gerar', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: corpo,
|
||||
});
|
||||
dados = await resposta.json();
|
||||
} catch (e) {
|
||||
feedback.textContent = 'Falha de rede — tente de novo.';
|
||||
feedback.classList.add('chave-feedback-erro');
|
||||
botao.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resposta.ok) {
|
||||
feedback.textContent = dados.erro || 'Não foi possível gerar a chave.';
|
||||
feedback.classList.add('chave-feedback-erro');
|
||||
botao.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(dados.chave);
|
||||
feedback.textContent = 'Copiado!';
|
||||
} catch (e) {
|
||||
feedback.textContent = 'Chave gerada, mas não copiou automaticamente.';
|
||||
feedback.classList.add('chave-feedback-erro');
|
||||
}
|
||||
botao.disabled = false;
|
||||
setTimeout(() => {
|
||||
feedback.textContent = '';
|
||||
feedback.classList.remove('chave-feedback-erro');
|
||||
}, 1500);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -187,6 +187,23 @@ def test_listar_clientes_inclui_csrf_token_no_contexto(monkeypatch):
|
||||
assert 'const CSRF_TOKEN = "";' in resposta.text
|
||||
|
||||
|
||||
def test_listar_clientes_tem_botao_de_gerar_chave_por_linha(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
"listar_clientes",
|
||||
lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [
|
||||
{"ID": 1, "CODIGO": "000042", "RAZAO": "Cliente Um", "FANTASIA": "Um", "CNPJ": "11222333000181"}
|
||||
],
|
||||
)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 1)
|
||||
|
||||
resposta = client.get("/clientes")
|
||||
assert resposta.status_code == 200
|
||||
assert 'data-codigo="000042"' in resposta.text
|
||||
assert "gerarChaveReativacao(this)" in resposta.text
|
||||
assert "/gerador-chave/gerar" in resposta.text
|
||||
|
||||
|
||||
def test_listar_clientes_pagina_com_controles_de_paginacao(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
|
||||
Reference in New Issue
Block a user