Files
GTI_GERENCIADOR_FISCAL/app/templates/gerador_chave.html
T
GustavoandClaude Sonnet 5 9012795973 fix: corrige contraste AA do laranja de acento no tema claro
- --cor-acento-contraste passa de branco pra #17171a (texto escuro sobre
  fundo laranja em botoes, unificando com o tema escuro) - contraste ~7:1
- novo token --cor-acento-texto (#B3510C no tema claro, #F5821F no
  tema escuro) usado para laranja como cor de TEXTO sobre fundo claro
  (link padrao, badge-acento, item ativo da sidebar, chave gerada em
  gerador_chave.html) - contraste 5.12:1 contra branco, acima do
  minimo AA de 4.5:1

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 00:48:12 -03:00

147 lines
6.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block titulo %}Gerador de Chave{% endblock %}
{% block conteudo %}
<div class="cartao" style="max-width:420px;margin:0 auto;">
<h1 style="text-align:center;">Gerador de Chave</h1>
<label for="tipo">Tipo de chave</label>
<select id="tipo">
<option value="reativacao">Reativação (mês/ano)</option>
<option value="instalacao">Instalação (aluguel/compra)</option>
</select>
<label for="codigo">Código do cliente</label>
<input type="text" id="codigo" placeholder="Ex: 000123" maxlength="6" inputmode="numeric">
<div id="campos-reativacao">
<label for="referencia">Mês/ano de referência</label>
<input type="month" id="referencia" min="2000-01" max="2100-12">
</div>
<div id="campos-instalacao" style="display:none;">
<label for="modalidade">Modalidade</label>
<select id="modalidade">
<option value="ALUGUEL">Aluguel</option>
<option value="COMPRA">Compra</option>
</select>
</div>
<button id="gerar">Gerar chave</button>
<div id="erro" style="color:var(--cor-perigo);margin-top:14px;display:none;"></div>
<div id="resultado" style="margin-top:20px;padding:14px;border-radius:8px;background:var(--cor-superficie-alt);border:1px solid var(--cor-borda);border-left:3px solid var(--cor-acento);word-break:break-all;font-family:'Courier New',monospace;color:var(--cor-acento-texto);display:none;"></div>
<button id="copiar" style="display:none;background:transparent;border:1px solid var(--cor-borda);color:var(--cor-texto-secundario);width:auto;padding:6px 10px;">Copiar</button>
</div>
<script>
const CHAVE_INTERNA =
"YUQL23KL23DF90WI5E1JAS467NMCXXL6JAOAUWWMCL0AOMM4A4VZYW9KHJUI2347" +
"EJHJKDF3424SKL K3LAKDJSL9RTIKJ";
// ==========================
// REATIVAÇÃO — mês/cliente/ano cifrados por inteiro (22 caracteres)
// ==========================
function gerarReativacao(codigoCliente, mes, ano, byteInicial = null) {
const cliente = String(codigoCliente).padStart(6, '0');
const texto = String(mes).padStart(2, '0') + cliente + String(ano).slice(-2);
const aleatorio = byteInicial !== null ? byteInicial : Math.floor(Math.random() * 256);
let anterior = aleatorio;
const resultado = [aleatorio];
const bytes = [];
for (let i = 0; i < texto.length; i++) bytes.push(texto.charCodeAt(i) & 0xff);
for (let i = 0; i < bytes.length; i++) {
const valor = (bytes[i] + anterior) % 255;
const cifrado = valor ^ CHAVE_INTERNA.charCodeAt(i % CHAVE_INTERNA.length);
resultado.push(cifrado);
anterior = cifrado;
}
return resultado.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join('');
}
// ==========================
// INSTALAÇÃO — só a modalidade ("30"=ALUGUEL / "00"=COMPRA) é cifrada
// (mesmo algoritmo de cifra da reativação, texto diferente); o valor
// derivado do código do cliente (cliente×19+31) entra sem cifrar, no
// meio dos 6 caracteres cifrados (3 antes + 3 depois).
// ==========================
function gerarInstalacao(codigoCliente, modalidade, byteInicial = null) {
const clienteNum = parseInt(String(codigoCliente), 10) || 0;
const valorCliente = clienteNum * 19 + 31;
const textoModalidade = modalidade === 'ALUGUEL' ? '30' : '00';
const aleatorio = byteInicial !== null ? byteInicial : Math.floor(Math.random() * 256);
let anterior = aleatorio;
const bytes = [aleatorio];
for (let i = 0; i < textoModalidade.length; i++) {
const valor = (textoModalidade.charCodeAt(i) + anterior) % 255;
const cifrado = valor ^ CHAVE_INTERNA.charCodeAt(i % CHAVE_INTERNA.length);
bytes.push(cifrado);
anterior = cifrado;
}
const criptografado = bytes.map(v => v.toString(16).padStart(2, '0').toUpperCase()).join('');
const parte1 = criptografado.substring(0, 3);
const parte2 = criptografado.substring(3, 6);
return `${parte1}${valorCliente}${parte2}`;
}
const $tipo = document.getElementById('tipo');
const $codigo = document.getElementById('codigo');
const $referencia = document.getElementById('referencia');
const $modalidade = document.getElementById('modalidade');
const $camposReativacao = document.getElementById('campos-reativacao');
const $camposInstalacao = document.getElementById('campos-instalacao');
const $erro = document.getElementById('erro');
const $resultado = document.getElementById('resultado');
const $copiar = document.getElementById('copiar');
(function preencherMesAtual() {
const hoje = new Date();
$referencia.value = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`;
})();
$tipo.addEventListener('change', () => {
const ehReativacao = $tipo.value === 'reativacao';
$camposReativacao.style.display = ehReativacao ? 'block' : 'none';
$camposInstalacao.style.display = ehReativacao ? 'none' : 'block';
});
function mostrarErro(msg) {
$erro.textContent = msg;
$erro.style.display = 'block';
$resultado.style.display = 'none';
$copiar.style.display = 'none';
}
document.getElementById('gerar').addEventListener('click', () => {
$erro.style.display = 'none';
const codigoCliente = String($codigo.value || '').trim();
if (!/^[0-9]{1,6}$/.test(codigoCliente)) return mostrarErro('Código do cliente deve ter até 6 dígitos numéricos.');
const codigoFormatado = codigoCliente.padStart(6, '0');
let chave;
if ($tipo.value === 'reativacao') {
if (!/^\d{4}-\d{2}$/.test($referencia.value)) return mostrarErro('Selecione o mês/ano de referência.');
const [anoStr, mesStr] = $referencia.value.split('-');
const mesNum = Number(mesStr), anoNum = Number(anoStr);
if (!(mesNum >= 1 && mesNum <= 12)) return mostrarErro('Mês fora do intervalo 1-12.');
if (!(anoNum >= 2000 && anoNum <= 2100)) return mostrarErro('Ano inválido (use entre 2000 e 2100).');
chave = gerarReativacao(codigoFormatado, mesNum, anoNum);
} else {
chave = gerarInstalacao(codigoFormatado, $modalidade.value);
}
$resultado.textContent = chave;
$resultado.style.display = 'block';
$copiar.style.display = 'inline-block';
});
$copiar.addEventListener('click', () => {
navigator.clipboard.writeText($resultado.textContent).then(() => {
$copiar.textContent = 'Copiado!';
setTimeout(() => { $copiar.textContent = 'Copiar'; }, 1500);
});
});
</script>
{% endblock %}