Files
GTI_GERENCIADOR_FISCAL/central/templates/agent_new_installation.html
T

120 lines
7.4 KiB
HTML

{% extends "base.html" %}
{% block title %}Nova instalação | GTI Fiscal{% endblock %}
{% block content %}
<div class="page-heading"><div><p class="eyebrow">DISTRIBUIÇÃO DO AGENT</p><h1>Nova instalação</h1><p class="muted">Cria uma instalação pendente; o pacote e o vínculo de enrollment só existem depois de gerados aqui.</p></div><div><a href="/agents" class="secondary">Voltar</a></div></div>
<section class="panel" style="max-width:560px">
{% if error %}<p class="error" role="alert">{{ error }}</p>{% endif %}
{% if not releases %}
<p class="muted">Nenhuma versão do Agent está publicada ainda. Publique uma release primeiro (<code>python central/scripts/publish_agent_release.py</code>) e ela aparecerá aqui.</p>
{% elif not tenants %}
<p class="muted">Nenhum cliente ativo encontrado no GTI Gerenciador. Verifique o cadastro por lá, ou rode a sincronização (<code>python central/scripts/sync_clients_from_gerenciador.py</code>).</p>
{% else %}
<form method="post" action="/agents/new">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="combo-field">
<label for="cliente-busca">Cliente</label>
<div class="combo" id="combo-cliente">
<input type="text" id="cliente-busca" placeholder="Buscar por nome, código ou CNPJ..." autocomplete="off" role="combobox" aria-expanded="false" aria-controls="cliente-lista" aria-autocomplete="list">
<input type="hidden" name="client_id" id="cliente-id" value="">
<ul id="cliente-lista" class="combo-lista" role="listbox" hidden></ul>
</div>
<p class="error combo-erro" id="cliente-erro" role="alert" hidden>Selecione um cliente da lista.</p>
</div>
<script type="application/json" id="clientes-json">{{ clients_json|safe }}</script>
<label>Nome da instalação
<input name="display_name" placeholder="ex. Servidor Principal" maxlength="160" required>
</label>
<label>Hostname esperado (opcional)
<input name="expected_hostname" placeholder="ex. SRV-CONSTRUCAO" maxlength="255">
</label>
<label>Versão do Agent
<select name="release_id" required>
{% for release in releases %}<option value="{{ release.id }}" {% if loop.first %}selected{% endif %}>{{ release.version }} ({{ release.platform }}/{{ release.architecture }})</option>{% endfor %}
</select>
</label>
<fieldset class="features">
<legend>Funcionalidades desta instalação</legend>
<input type="hidden" name="features_selected" value="1">
<label><input type="checkbox" name="feature_corrector" value="1" checked> Corretor fiscal (sincroniza e corrige o cadastro existente)</label>
<label>Enriquecimento de produtos novos
<select name="feature_enrichment_mode">
<option value="DISABLED" selected>Desativado</option>
<option value="DRY_RUN">Simulação (não grava)</option>
<option value="ENABLED">Automático</option>
</select>
</label>
<small class="muted">Nada é processado até as funcionalidades estarem definidas. Pode ser alterado depois, no detalhe do Agent.</small>
</fieldset>
<p><button type="submit">Criar instalação</button></p>
</form>
<style>
.combo { position: relative; }
.combo-lista { position: absolute; z-index: 20; left: 0; right: 0; top: 100%; margin: 4px 0 0; padding: 4px; list-style: none; max-height: 280px; overflow-y: auto; background: var(--cor-superficie); border: 1px solid var(--cor-borda); border-radius: 8px; box-shadow: var(--sombra-card); }
.combo-lista li { padding: 8px 10px; border-radius: 6px; cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.combo-lista li small { color: var(--cor-texto-secundario); }
.combo-lista li:hover, .combo-lista li.ativo { background: var(--cor-superficie-alt); }
.combo-lista li.selecionado { background: var(--cor-acento-suave); color: var(--cor-acento-texto); font-weight: 600; }
.combo-lista li.vazio { cursor: default; color: var(--cor-texto-secundario); }
.combo.invalido input[type=text] { border-color: var(--cor-perigo); }
.combo-field { margin-bottom: 12px; }
</style>
<script>
(function () {
var clientes = JSON.parse(document.getElementById("clientes-json").textContent);
var box = document.getElementById("cliente-busca"), hid = document.getElementById("cliente-id");
var lista = document.getElementById("cliente-lista"), erro = document.getElementById("cliente-erro");
var combo = document.getElementById("combo-cliente"), form = box.form;
var visiveis = [], ativo = -1;
function norm(t) { return (t || "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase(); }
function cnpjFmt(d) {
if (!d) return "";
if (d.length === 14) return d.replace(/^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/, "$1.$2.$3/$4-$5");
if (d.length === 11) return d.replace(/^(\d{3})(\d{3})(\d{3})(\d{2})$/, "$1.$2.$3-$4");
return d;
}
function abrir(v) { lista.hidden = !v; box.setAttribute("aria-expanded", v ? "true" : "false"); }
function render() {
var q = norm(box.value), qd = q.replace(/\D/g, "");
visiveis = clientes.filter(function (c) {
if (!q) return true;
return norm(c.name).indexOf(q) !== -1 || norm(c.code).indexOf(q) !== -1 || (qd && (c.cnpj || "").indexOf(qd) !== -1);
}).slice(0, 100);
lista.innerHTML = "";
if (!visiveis.length) {
var vazio = document.createElement("li"); vazio.className = "vazio"; vazio.textContent = "Nenhum cliente ativo encontrado."; lista.appendChild(vazio);
}
visiveis.forEach(function (c, i) {
var li = document.createElement("li"); li.setAttribute("role", "option"); li.dataset.i = i;
if (String(c.id) === hid.value) li.classList.add("selecionado");
var nome = document.createElement("span"); nome.textContent = c.name; li.appendChild(nome);
if (c.cnpj) { var sm = document.createElement("small"); sm.textContent = cnpjFmt(c.cnpj); li.appendChild(sm); }
li.addEventListener("mousedown", function (e) { e.preventDefault(); escolher(c); });
lista.appendChild(li);
});
ativo = -1; abrir(true);
}
function destacar(n) {
var itens = lista.querySelectorAll("li[role=option]"); if (!itens.length) return;
ativo = (n + itens.length) % itens.length;
itens.forEach(function (li, i) { li.classList.toggle("ativo", i === ativo); });
itens[ativo].scrollIntoView({ block: "nearest" });
}
function escolher(c) { box.value = c.name; hid.value = c.id; erro.hidden = true; combo.classList.remove("invalido"); abrir(false); }
box.addEventListener("input", function () { hid.value = ""; render(); });
box.addEventListener("focus", render);
box.addEventListener("blur", function () { setTimeout(function () { abrir(false); }, 120); });
box.addEventListener("keydown", function (e) {
if (e.key === "ArrowDown") { e.preventDefault(); if (lista.hidden) render(); destacar(ativo + 1); }
else if (e.key === "ArrowUp") { e.preventDefault(); destacar(ativo - 1); }
else if (e.key === "Enter") { if (!lista.hidden && ativo >= 0 && visiveis[ativo]) { e.preventDefault(); escolher(visiveis[ativo]); } else if (!hid.value) { e.preventDefault(); } }
else if (e.key === "Escape") { abrir(false); }
});
form.addEventListener("submit", function (e) {
if (!hid.value) { e.preventDefault(); erro.hidden = false; combo.classList.add("invalido"); box.focus(); }
});
})();
</script>
{% endif %}
</section>
{% endblock %}