Implandação da customização das permissões dos usuarios e das notificaçoes

This commit is contained in:
2026-08-26 17:24:25 -03:00
parent 808c2c7545
commit 5bb5d74e22
71 changed files with 2487 additions and 269 deletions

View File

@@ -82,22 +82,54 @@
</div>
<%# ── Variáveis ─────────────────────────────────────── %>
<%# ── EMOJIS ─────────────────────────────────────────
Digitar emoji no teclado do computador é o passo que trava o ADM;
aqui ele clica. A lista é curta e curada de propósito — um seletor
completo viraria uma biblioteca externa (e CSP) para inserir 3
caracteres. %>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4 mb-4">
<p class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">Emojis</p>
<div class="flex flex-wrap gap-1">
<% %w[✅ ❌ ⚠️ 📌 📢 💰 💵 🧾 📅 🕒 🚚 📦 📍 👤 👍 🙏 ⭐ 🔴 🟢 🟡 ➡️ 📄 📷 🔔 ✍️ 😀 🎉].each do |emoji| %>
<button type="button" class="chip-emoji w-9 h-9 rounded-lg bg-[#0a0a0a] border border-white/10
hover:border-[#f97316] text-lg leading-none"
data-emoji="<%= emoji %>" title="Inserir <%= emoji %>"><%= emoji %></button>
<% end %>
</div>
</div>
<div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4">
<p class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">Variáveis</p>
<p class="text-xs text-gray-500 mb-3">
Clique num campo da mensagem e depois numa variável para inseri-la ali.
</p>
<div class="flex flex-wrap gap-1.5">
<% @variaveis.each do |nome, meta| %>
<button type="button" title="<%= meta[:descricao] %> (ex.: <%= meta[:amostra] %>)"
class="chip-variavel px-2.5 py-1 rounded-full bg-[#f97316]/10 border border-[#f97316]/30
text-xs font-mono text-[#f97316] hover:bg-[#f97316]/20 hover:border-[#f97316]
transition-colors cursor-pointer"
data-variavel="<%= nome %>">
<%= "{{#{nome}}}" %>
</button>
<% end %>
</div>
<%# Agrupadas por ORIGEM e sem esconder o que não se aplica: a variável
de outro gatilho aparece apagada, com o aviso de que sai em branco.
Antes, a tela só listava as 6 do contexto — qualquer outro dado
exigia deploy. %>
<% @variaveis.each do |origem, itens| %>
<% next if itens.empty? %>
<p class="text-[11px] uppercase tracking-wider text-gray-500 mt-3 mb-1.5">
<%= Notificacao::CatalogoVariaveis::ORIGENS[origem] %>
</p>
<div class="flex flex-wrap gap-1.5">
<% itens.each do |v| %>
<button type="button"
title="<%= v[:descricao] %> (ex.: <%= v[:amostra] %>)<%= ' — não existe neste gatilho: sai em branco' unless v[:aplicavel] %>"
class="chip-variavel px-2.5 py-1 rounded-full text-xs font-mono transition-colors cursor-pointer
<%= v[:aplicavel] ? 'bg-[#f97316]/10 border border-[#f97316]/30 text-[#f97316] hover:bg-[#f97316]/20 hover:border-[#f97316]' : 'bg-white/5 border border-white/10 text-gray-500 hover:border-gray-500' %>"
data-variavel="<%= v[:chave] %>">
{{<%= v[:chave] %>}}<%= ' ·' unless v[:aplicavel] %>
</button>
<% end %>
</div>
<% end %>
<p class="text-[11px] text-gray-500 mt-3 leading-relaxed">
As apagadas (·) não existem neste gatilho e sairiam em branco.
<%= link_to 'Criar variável própria', admin_variaveis_path, class: 'text-orange-500 hover:underline' %>.
</p>
</div>
</div>
@@ -312,20 +344,29 @@
});
}
// ── Variáveis: insere no cursor do último campo focado ─────
// ── Inserção no cursor do último campo focado ──────────────
// Serve para variável e para emoji: a mecânica é a mesma (recortar o valor
// na posição do cursor e reemitir `input` para o JSON do editor atualizar).
function inserirNoCampo(texto) {
if (!ultimoCampo) { alert('Clique primeiro no campo onde o texto deve entrar.'); return; }
var ini = ultimoCampo.selectionStart != null ? ultimoCampo.selectionStart : ultimoCampo.value.length;
var fim = ultimoCampo.selectionEnd != null ? ultimoCampo.selectionEnd : ini;
ultimoCampo.value = ultimoCampo.value.slice(0, ini) + texto + ultimoCampo.value.slice(fim);
ultimoCampo.dispatchEvent(new Event('input', { bubbles: true }));
ultimoCampo.focus();
ultimoCampo.selectionStart = ultimoCampo.selectionEnd = ini + texto.length;
}
document.querySelectorAll('.chip-variavel').forEach(function (chip) {
chip.addEventListener('click', function () {
var texto = '{{' + chip.dataset.variavel + '}}';
if (!ultimoCampo) { alert('Clique primeiro no campo onde a variável deve entrar.'); return; }
var ini = ultimoCampo.selectionStart != null ? ultimoCampo.selectionStart : ultimoCampo.value.length;
var fim = ultimoCampo.selectionEnd != null ? ultimoCampo.selectionEnd : ini;
ultimoCampo.value = ultimoCampo.value.slice(0, ini) + texto + ultimoCampo.value.slice(fim);
ultimoCampo.dispatchEvent(new Event('input', { bubbles: true }));
ultimoCampo.focus();
ultimoCampo.selectionStart = ultimoCampo.selectionEnd = ini + texto.length;
inserirNoCampo('{{' + chip.dataset.variavel + '}}');
});
});
document.querySelectorAll('.chip-emoji').forEach(function (chip) {
chip.addEventListener('click', function () { inserirNoCampo(chip.dataset.emoji); });
});
// ── Preview: renderizado no servidor, pelo mesmo código do envio ──
var token = document.querySelector('meta[name="csrf-token"]');
document.getElementById('btn-preview').addEventListener('click', function () {