diff --git a/src/main/webapp/master/clientes.html b/src/main/webapp/master/clientes.html
index e47d5f5..b1dcc25 100644
--- a/src/main/webapp/master/clientes.html
+++ b/src/main/webapp/master/clientes.html
@@ -78,6 +78,91 @@
+
+
+
+
+
+
+
Detalhes do cliente
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Conta principal: utilize "Editar" para alterar os dados da empresa. Funcionários e contatos são gerenciados no acesso do próprio cliente.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Editar cliente
+
+
+
+
+
diff --git a/src/main/webapp/master/clientes.js b/src/main/webapp/master/clientes.js
index 60fbc49..9c7594c 100644
--- a/src/main/webapp/master/clientes.js
+++ b/src/main/webapp/master/clientes.js
@@ -1,5 +1,17 @@
-const empresaForm=document.querySelector('#empresa-form'),rows=document.querySelector('#rows'),busca=document.querySelector('#busca'),planoSelect=document.querySelector('#plano-select'),cadastroModal=document.querySelector('#cadastro-modal');let empresas=[],planos=[],modo=null,selecionado=null;
-let modoSlugAuto='sequencial';
+const empresaForm = document.querySelector('#empresa-form');
+const rows = document.querySelector('#rows');
+const busca = document.querySelector('#busca');
+const planoSelect = document.querySelector('#plano-select');
+const cadastroModal = document.querySelector('#cadastro-modal');
+const verModal = document.querySelector('#ver-modal');
+const editarModal = document.querySelector('#editar-modal');
+const editarForm = document.querySelector('#editar-form');
+
+let empresas = [];
+let planos = [];
+let clienteVisualizandoId = null;
+let clienteEditandoId = null;
+let modoSlugAuto = 'sequencial';
const Icons = {
edit: '',
@@ -13,7 +25,6 @@ function planoOptions(valor){return ''+planos
function renderPlanos(){planoSelect.innerHTML=planoOptions('')}
function match(e,q){if(!q)return true;return [e.slug,e.nome,e.cnpj,e.status,e.plano,e.api_key].join(' ').toLowerCase().includes(q.toLowerCase())}
function statusAction(e){const acao=e.status==='ativa'?'Bloquear':'Ativar';return ``}
-function detalheRow(e){if(selecionado!==e.id||!modo)return'';if(modo==='ver')return `${esc(e.nome)}Slug: ${esc(e.slug)} CNPJ: ${esc(e.cnpj||'-')} Status: ${esc(e.status)} Plano: ${e.plano?`${esc(e.plano)} ${e.plano_valor_centavos?'- '+Util.money(e.plano_valor_centavos):''}`:'-'} Conta principal: abra Editar para alterar dados da empresa; funcionarios ficam no acesso da empresa. |
`;return ` |
`}
function proximoSlugSequencial(){
const slugsExistentes = new Set(empresas.map(e => String(e.slug || '').trim().toLowerCase()));
@@ -67,10 +78,10 @@ function render(){
const q=busca.value.trim();
const lista=empresas.filter(e=>match(e,q));
rows.innerHTML=lista.length?lista.map(e=>`
- | ${e.slug} |
- ${e.nome} |
+ ${esc(e.slug)} |
+ ${esc(e.nome)} |
${statusAction(e)} |
- ${e.plano?`${e.plano} ${e.plano_valor_centavos?'- '+Util.money(e.plano_valor_centavos):''}`:'-'} |
+ ${e.plano?`${esc(e.plano)} ${e.plano_valor_centavos?'- '+Util.money(e.plano_valor_centavos):''}`:'-'} |
@@ -78,7 +89,7 @@ function render(){
|
-
${detalheRow(e)}`).join(''):'| Nenhum cliente encontrado. |
';
+ `).join(''):'| Nenhum cliente encontrado. |
';
}
async function load(){try{const [pe,de]=await Promise.all([Api.get('/api/master/planos'),Api.get('/api/master/empresas')]);planos=pe.planos||[];empresas=de.empresas||[];renderPlanos();render()}catch(e){Util.toast(e.message,true)}}
@@ -191,13 +202,130 @@ empresaForm.onsubmit=async e=>{
}
};
-busca.oninput=render;
-window.ver=id=>{selecionado=selecionado===id&&modo==='ver'?null:id;modo=selecionado?'ver':null;render()};
-window.editar=id=>{selecionado=selecionado===id&&modo==='editar'?null:id;modo=selecionado?'editar':null;render()};
-window.fecharDetalhe=()=>{selecionado=null;modo=null;render()};
-window.copiarApiKey=async id=>{const e=empresas.find(x=>Number(x.id)===Number(id));if(!e?.api_key)return;try{await navigator.clipboard.writeText(e.api_key);Util.toast('API Key copiada')}catch(_){Util.toast('Selecione e copie a API Key manualmente',true)}};
-window.solicitarAlteracaoApiKey=id=>{const e=empresas.find(x=>Number(x.id)===Number(id));Util.toast('Solicitacao de alteracao registrada para '+(e?.nome||'cliente'))};
-window.salvarEdicao=async(ev,id)=>{ev.preventDefault();try{await Api.put(`/api/master/empresas/${id}`,Util.formData(ev.target));Util.toast('Cliente atualizado');selecionado=null;modo=null;load()}catch(err){Util.toast(err.message,true)}};
-window.alterarStatus=async(id,status)=>{try{await Api.post(`/api/master/empresas/${id}/status`,{status});Util.toast(status==='ativa'?'Cliente ativado':'Cliente bloqueado');load()}catch(err){Util.toast(err.message,true)}};
-window.excluir=async id=>{try{await Api.request(`/api/master/empresas/${id}`,{method:'DELETE'});Util.toast('Cliente excluido');selecionado=null;modo=null;load()}catch(err){Util.toast(err.message,true)}};
+busca.oninput = render;
+
+// Visualização em Popup Modal
+window.ver = (id) => {
+ const emp = empresas.find(x => Number(x.id) === Number(id));
+ if (!emp) return;
+ clienteVisualizandoId = emp.id;
+ document.querySelector('#ver-modal-titulo').textContent = emp.nome || 'Detalhes do cliente';
+ document.querySelector('#ver-slug').value = emp.slug || '-';
+ document.querySelector('#ver-nome').value = emp.nome || '-';
+ document.querySelector('#ver-cnpj').value = emp.cnpj || '-';
+ document.querySelector('#ver-status').innerHTML = Util.badge(emp.status);
+ document.querySelector('#ver-plano').value = emp.plano ? `${emp.plano} ${emp.plano_valor_centavos ? '- ' + Util.money(emp.plano_valor_centavos) : ''}` : 'Sem plano';
+ document.querySelector('#ver-api-key').value = emp.api_key || '';
+ verModal?.classList.remove('hidden');
+};
+
+window.fecharModalVer = () => {
+ verModal?.classList.add('hidden');
+ clienteVisualizandoId = null;
+};
+
+window.copiarApiKeyModal = async () => {
+ const apiKey = document.querySelector('#ver-api-key')?.value;
+ if (!apiKey) return;
+ try {
+ await navigator.clipboard.writeText(apiKey);
+ Util.toast('API Key copiada com sucesso!');
+ } catch (_) {
+ Util.toast('Selecione e copie a API Key manualmente', true);
+ }
+};
+
+window.solicitarAlteracaoApiKeyModal = () => {
+ const emp = empresas.find(x => Number(x.id) === Number(clienteVisualizandoId));
+ Util.toast('Solicitação de alteração registrada para ' + (emp?.nome || 'cliente'));
+};
+
+window.abrirEditarDeVer = () => {
+ const id = clienteVisualizandoId;
+ fecharModalVer();
+ if (id) editar(id);
+};
+
+// Edição em Popup Modal
+window.editar = (id) => {
+ const emp = empresas.find(x => Number(x.id) === Number(id));
+ if (!emp) return;
+ clienteEditandoId = emp.id;
+ editarForm.reset();
+ const errEl = document.querySelector('#editar-modal-error-msg');
+ if (errEl) { errEl.textContent = ''; errEl.classList.add('hidden'); }
+ document.querySelector('#editar-modal-titulo').textContent = `Editar: ${emp.nome}`;
+ document.querySelector('#editar-id').value = emp.id;
+ document.querySelector('#editar-slug').value = emp.slug || '';
+ document.querySelector('#editar-nome').value = emp.nome || '';
+ document.querySelector('#editar-cnpj').value = emp.cnpj || '';
+ document.querySelector('#editar-plano-select').innerHTML = planoOptions(emp.plano_id);
+ editarModal?.classList.remove('hidden');
+ document.querySelector('#editar-nome')?.focus();
+};
+
+window.fecharModalEditar = () => {
+ editarModal?.classList.add('hidden');
+ clienteEditandoId = null;
+};
+
+window.salvarEdicaoModal = async (ev) => {
+ ev.preventDefault();
+ if (!clienteEditandoId) return;
+ try {
+ const dados = Util.formData(editarForm);
+ await Api.put(`/api/master/empresas/${clienteEditandoId}`, dados);
+ Util.toast('Cliente atualizado com sucesso!');
+ fecharModalEditar();
+ load();
+ } catch (err) {
+ const errEl = document.querySelector('#editar-modal-error-msg');
+ if (errEl) {
+ errEl.textContent = err.message || 'Erro ao atualizar cliente';
+ errEl.classList.remove('hidden');
+ }
+ Util.toast(err.message, true);
+ }
+};
+
+// Exclusão com confirmação em Popup Modal
+window.excluir = async (id) => {
+ const emp = empresas.find(x => Number(x.id) === Number(id));
+ const nome = emp ? ` "${emp.nome}"` : '';
+ if (!await Util.confirm(`Tem certeza que deseja excluir o cliente${nome}?`, 'Excluir cliente')) return;
+ try {
+ await Api.request(`/api/master/empresas/${id}`, { method: 'DELETE' });
+ Util.toast('Cliente excluído com sucesso');
+ load();
+ } catch (err) {
+ Util.toast(err.message, true);
+ }
+};
+
+window.copiarApiKey = async (id) => {
+ const e = empresas.find(x => Number(x.id) === Number(id));
+ if (!e?.api_key) return;
+ try {
+ await navigator.clipboard.writeText(e.api_key);
+ Util.toast('API Key copiada');
+ } catch (_) {
+ Util.toast('Selecione e copie a API Key manualmente', true);
+ }
+};
+
+window.solicitarAlteracaoApiKey = (id) => {
+ const e = empresas.find(x => Number(x.id) === Number(id));
+ Util.toast('Solicitação de alteração registrada para ' + (e?.nome || 'cliente'));
+};
+
+window.alterarStatus = async (id, status) => {
+ try {
+ await Api.post(`/api/master/empresas/${id}/status`, { status });
+ Util.toast(status === 'ativa' ? 'Cliente ativado' : 'Cliente bloqueado');
+ load();
+ } catch (err) {
+ Util.toast(err.message, true);
+ }
+};
+
load();