148 lines
7.3 KiB
Plaintext
148 lines
7.3 KiB
Plaintext
<%= form_with model: [:admin, contato], data: { turbo: false }, class: 'space-y-5' do |f| %>
|
|
<% if contato.errors.any? %>
|
|
<div class="bg-red-500/10 border border-red-500/30 rounded-xl px-4 py-3 text-red-300 text-sm">
|
|
<ul class="list-disc list-inside space-y-1">
|
|
<% contato.errors.full_messages.each do |msg| %><li><%= msg %></li><% end %>
|
|
</ul>
|
|
</div>
|
|
<% end %>
|
|
|
|
<%# Pessoa x grupo do WhatsApp. O JavaScript no fim troca os blocos; sem JS a
|
|
página continua utilizável — os dois blocos ficam visíveis e o model limpa
|
|
o que não pertence ao tipo escolhido. %>
|
|
<div data-tipo-contato>
|
|
<%= f.label :tipo, 'Tipo de destinatário', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.select :tipo,
|
|
[['Pessoa (número ou e-mail)', 'pessoa'], ['Grupo do WhatsApp', 'grupo_whatsapp']], {},
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500',
|
|
id: 'contato-tipo' %>
|
|
</div>
|
|
|
|
<div>
|
|
<%= f.label :nome, class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.text_field :nome, required: true,
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500' %>
|
|
<p class="text-xs text-gray-500 mt-1">Como aparece nas listas e em <code>{{contato}}</code>.</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4" id="bloco-pessoa">
|
|
<div>
|
|
<%= f.label :telefone, 'WhatsApp', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.text_field :telefone, placeholder: '(11) 92005-1157',
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500' %>
|
|
<p class="text-xs text-gray-500 mt-1">Pode digitar com máscara — é normalizado ao salvar.</p>
|
|
</div>
|
|
<div>
|
|
<%= f.label :email, 'E-mail', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.email_field :email,
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500' %>
|
|
</div>
|
|
</div>
|
|
|
|
<%# O identificador do grupo é opaco ("120363…@g.us") — digitar na mão é pedir
|
|
erro. A lista vem da ponte, com o número já conectado. %>
|
|
<div id="bloco-grupo" data-url="<%= grupos_whatsapp_admin_contatos_path %>">
|
|
<%= f.label :whatsapp_grupo_jid, 'Grupo do WhatsApp', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<div class="flex gap-2">
|
|
<select id="grupo-jid" name="contato[whatsapp_grupo_jid]"
|
|
class="flex-1 bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500">
|
|
<% if contato.whatsapp_grupo_jid.present? %>
|
|
<option value="<%= contato.whatsapp_grupo_jid %>" selected>
|
|
<%= contato.whatsapp_grupo_nome.presence || contato.whatsapp_grupo_jid %>
|
|
</option>
|
|
<% else %>
|
|
<option value="">— carregue a lista —</option>
|
|
<% end %>
|
|
</select>
|
|
<button type="button" id="btn-carregar-grupos"
|
|
class="px-4 py-2.5 rounded-xl text-sm whitespace-nowrap bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500">
|
|
Buscar grupos
|
|
</button>
|
|
</div>
|
|
<%= f.hidden_field :whatsapp_grupo_nome, id: 'grupo-nome' %>
|
|
<p class="text-xs text-gray-500 mt-1" id="grupo-aviso">
|
|
Só aparecem grupos em que o número conectado já é membro. Grupo com "somente
|
|
administradores" recusa o envio se o número não for admin.
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<%= f.label :grupo_contato_id, 'Grupo', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.collection_select :grupo_contato_id, @grupos, :id, :nome, { include_blank: 'Sem grupo (não recebe nada)' },
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500' %>
|
|
<p class="text-xs text-gray-500 mt-1">É o grupo que define quais eventos este contato recebe.</p>
|
|
</div>
|
|
|
|
<div>
|
|
<%= f.label :observacao, 'Observação', class: 'block text-gray-400 text-sm mb-1' %>
|
|
<%= f.text_field :observacao,
|
|
class: 'w-full bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500' %>
|
|
</div>
|
|
|
|
<label class="flex items-center gap-2 text-sm text-gray-300">
|
|
<%= f.check_box :ativo, class: 'rounded border-white/20 bg-[#1a1a1a] text-orange-500' %>
|
|
Ativo
|
|
</label>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<%= f.submit 'Salvar', class: 'px-4 py-2.5 rounded-xl text-sm font-semibold bg-orange-500 text-black hover:bg-orange-400 cursor-pointer' %>
|
|
<%= link_to 'Cancelar', admin_contatos_path, data: { turbo: false }, class: 'text-gray-400 hover:text-white text-sm' %>
|
|
</div>
|
|
<% end %>
|
|
|
|
<script>
|
|
(function () {
|
|
var tipo = document.getElementById('contato-tipo');
|
|
var pessoa = document.getElementById('bloco-pessoa');
|
|
var grupo = document.getElementById('bloco-grupo');
|
|
var select = document.getElementById('grupo-jid');
|
|
var oculto = document.getElementById('grupo-nome');
|
|
var aviso = document.getElementById('grupo-aviso');
|
|
var botao = document.getElementById('btn-carregar-grupos');
|
|
if (!tipo || !pessoa || !grupo) return;
|
|
|
|
function alternar() {
|
|
var ehGrupo = tipo.value === 'grupo_whatsapp';
|
|
pessoa.classList.toggle('hidden', ehGrupo);
|
|
grupo.classList.toggle('hidden', !ehGrupo);
|
|
}
|
|
tipo.addEventListener('change', alternar);
|
|
alternar();
|
|
|
|
// O nome do grupo é gravado junto com o id: a tela precisa mostrar algo
|
|
// legível mesmo com a ponte fora do ar (o id sozinho não diz nada).
|
|
select.addEventListener('change', function () {
|
|
var opcao = select.options[select.selectedIndex];
|
|
oculto.value = opcao ? opcao.dataset.nome || opcao.textContent.trim() : '';
|
|
});
|
|
|
|
botao.addEventListener('click', function () {
|
|
botao.disabled = true;
|
|
botao.textContent = 'Buscando…';
|
|
fetch(grupo.dataset.url, { headers: { Accept: 'application/json' } })
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (dados) {
|
|
if (dados.erro) { aviso.textContent = dados.erro; return; }
|
|
if (!dados.grupos.length) {
|
|
aviso.textContent = 'Nenhum grupo encontrado — o número conectado não participa de nenhum.';
|
|
return;
|
|
}
|
|
select.innerHTML = '<option value="">— escolha —</option>';
|
|
dados.grupos.forEach(function (g) {
|
|
var o = document.createElement('option');
|
|
o.value = g.id;
|
|
o.dataset.nome = g.nome;
|
|
// Avisar ANTES sobre grupo restrito: sem isso o envio falharia e o
|
|
// erro só apareceria depois, no log.
|
|
var alerta = (g.somente_admin && !g.sou_admin) ? ' ⚠️ só admin escreve' : '';
|
|
o.textContent = g.nome + ' (' + g.participantes + ')' + alerta;
|
|
select.appendChild(o);
|
|
});
|
|
aviso.textContent = dados.grupos.length + ' grupo(s) encontrado(s).';
|
|
})
|
|
.catch(function () { aviso.textContent = 'Não foi possível falar com a ponte do WhatsApp.'; })
|
|
.finally(function () { botao.disabled = false; botao.textContent = 'Buscar grupos'; });
|
|
});
|
|
})();
|
|
</script>
|