1import { api } from '../api.js'; 2import { esc } from '../utils.js'; 3import { modalShow, closeModal, confirmModal } from '../ui/modal.js'; 4import { toast } from '../ui/toast.js'; 5import { isMaster } from '../state.js'; 6 7export async function viewAcessos() { 8 if (!isMaster()) return '<div class="empty">Apenas o acesso Master.</div>'; 9 const res = await api.get('/usuarios'); 10 const items = res.items || []; 11 setTimeout(() => bind(items), 0); 12 return `<div class="card"><div class="hd"><h3>Usuários do sistema</h3><div class="sp"></div> 13 <button class="btn pri sm" data-new-user>+ Novo</button> 14 </div> 15 <div class="bd" style="overflow:auto"><table> 16 <thead><tr><th>Nome</th><th>Login</th><th>Papel</th><th>Empresa</th><th>Ações</th></tr></thead> 17 <tbody>${items.map(u => ` 18 <tr> 19 <td>${esc(u.nome)}</td> 20 <td>${esc(u.login)}</td> 21 <td>${esc(u.role)}</td> 22 <td>${u.empresa_id || 'â'}</td> 23 <td> 24 <button class="btn sm" data-edit-user="${u.id}">Editar</button> 25 ${u.ativo ? `<button class="btn sm danger" data-deact="${u.id}">Desativar</button>` : '<span class="mini">inativo</span>'} 26 </td> 27 </tr>`).join('') || '<tr><td colspan=5 class=empty>Sem usuários.</td></tr>'} 28 </tbody> 29 </table></div> 30 </div>`; 31} 32 33function bind(items) { 34 const usersById = new Map(items.map(u => [u.id, u])); 35 document.querySelector('[data-new-user]')?.addEventListener('click', () => modalUser(null)); 36 document.querySelectorAll('[data-edit-user]').forEach(el => { 37 el.addEventListener('click', () => modalUser(+el.dataset.editUser, usersById.get(+el.dataset.editUser))); 38 }); 39 document.querySelectorAll('[data-deact]').forEach(el => { 40 el.addEventListener('click', async () => { 41 if (!(await confirmModal('Desativar este usuário?', { title: 'Desativar usuário', okText: 'Desativar' }))) return; 42 try { 43 await api.delete('/usuarios/' + el.dataset.deact); 44 toast('Usuário desativado'); 45 import('../router.js').then(m => m.render()); 46 } catch (e) { toast('Erro: ' + e.message); } 47 }); 48 }); 49} 50 51async function modalUser(id, u = null) { 52 const isNew = !id; 53 const nome = u?.nome || ''; 54 modalShow(isNew ? 'Novo usuário' : 'Editar usuário', ` 55 <div class="field"><label for="u_nome">Nome</label><input id="u_nome" value="${esc(nome)}"></div> 56 ${isNew ? '<div class="field"><label for="u_login">Login</label><input id="u_login"></div>' : ''} 57 <div class="field"><label for="u_senha">${isNew ? 'Senha' : 'Nova senha (em branco para manter)'}</label><input id="u_senha" type="password" autocomplete="new-password"></div> 58 ${isNew ? `<div class="field"><label for="u_role">Papel</label><select id="u_role"><option value="operador">Operador</option><option value="master">Master</option></select></div>` : ''}`, 59 [ 60 { t:'Salvar', cls:'pri', fn: async () => { 61 try { 62 const nomeNovo = document.getElementById('u_nome').value.trim(); 63 if (!nomeNovo) { toast('Nome não pode ficar em branco'); return; } 64 const body = {}; 65 // Só envia `nome` se realmente mudou â evita sobrescrever quando o usuário 66 // só quer resetar a senha e não tocou no campo. 67 if (isNew || nomeNovo !== nome) body.nome = nomeNovo; 68 const senha = document.getElementById('u_senha').value; 69 if (senha) body.senha = senha; 70 if (isNew) { 71 body.login = document.getElementById('u_login').value.trim(); 72 body.role = document.getElementById('u_role').value; 73 if (!body.login || !body.senha) { toast('Login e senha obrigatórios'); return; } 74 await api.post('/usuarios', body); 75 } else { 76 if (!Object.keys(body).length) { toast('Nada pra atualizar'); return; } 77 await api.patch('/usuarios/' + id, body); 78 } 79 closeModal(); 80 toast(isNew ? 'Criado' : 'Atualizado'); 81 import('../router.js').then(m => m.render()); 82 } catch (e) { toast('Erro: ' + (e.body?.error || e.message)); } 83 }}, 84 { t:'Cancelar', fn: closeModal }, 85 ]); 86}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.