Adiciona respostas por condominio
This commit is contained in:
@@ -65,11 +65,13 @@ public final class ClienteHandlers implements HttpHandler {
|
||||
if(p.length==3&&"config".equals(p[0])&&"evolution-instancia".equals(p[1])&&"desconectar".equals(p[2])&&"POST".equals(method)){ desconectarEvolution(ex,empresaId); return; }
|
||||
if(p.length==3&&"config".equals(p[0])&&"evolution-instancia".equals(p[1])&&"reconectar".equals(p[2])&&"POST".equals(method)){ reconectarEvolution(ex,empresaId); return; }
|
||||
if(p.length==1&&"assembleias-calendario".equals(p[0])&&"GET".equals(method)){ listarAssembleiasCalendario(ex,c,empresaId,cargo,funcionarioId); return; }
|
||||
if(p.length==1&&"respostas".equals(p[0])&&"POST".equals(method)){ registrarResposta(ex,c,funcionarioId,null); return; }
|
||||
if(p.length==3&&"condominios".equals(p[0])&&"avisos".equals(p[2])){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} if("POST".equals(method)) criarAviso(ex,c,empresaId,cond,funcionarioId); else if("GET".equals(method)) listarAvisos(ex,c,cond); else HttpResponses.methodNotAllowed(ex); return; }
|
||||
if(p.length==4&&"condominios".equals(p[0])&&"avisos".equals(p[2])){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} if("GET".equals(method)) detalheAviso(ex,c,cond,id(p[3])); else if("DELETE".equals(method)) excluirAviso(ex,c,empresaId,cond,id(p[3]),funcionarioId); else HttpResponses.methodNotAllowed(ex); return; }
|
||||
if(p.length==5&&"condominios".equals(p[0])&&"avisos".equals(p[2])&&"reenviar".equals(p[4])&&"POST".equals(method)){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} reenviarAviso(ex,c,empresaId,cond,id(p[3])); return; }
|
||||
if(p.length==1&&"condominios".equals(p[0])){ if("POST".equals(method)) criarCondominio(ex,c,funcionarioId,cargo); else if("GET".equals(method)) listarCondominios(ex,c,funcionarioId,cargo); else HttpResponses.methodNotAllowed(ex); return; }
|
||||
if(p.length==2&&"condominios".equals(p[0])){ long id=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,id)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} if("GET".equals(method)) detalheCondominio(ex,c,id); else if("PUT".equals(method)) atualizarCondominio(ex,c,id,funcionarioId); else HttpResponses.methodNotAllowed(ex); return; }
|
||||
if(p.length==3&&"condominios".equals(p[0])&&"respostas".equals(p[2])){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} if("GET".equals(method)) listarRespostas(ex,c,cond); else if("POST".equals(method)) registrarResposta(ex,c,funcionarioId,cond); else HttpResponses.methodNotAllowed(ex); return; }
|
||||
if(p.length==1&&"contatos".equals(p[0])&&"POST".equals(method)){ criarContato(ex,c,funcionarioId); return; }
|
||||
if(p.length==3&&"condominios".equals(p[0])&&"contatos".equals(p[2])&&"GET".equals(method)){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} listarContatos(ex,c,cond); return; }
|
||||
if(p.length==4&&"condominios".equals(p[0])&&"contatos".equals(p[2])&&"vincular".equals(p[3])&&"POST".equals(method)){ long cond=id(p[1]); if(!podeAcessarCondominio(c,cargo,funcionarioId,cond)){HttpResponses.json(ex,403,Map.of("erro","condominio_nao_vinculado"));return;} vincularContato(ex,c,cond,funcionarioId); return; }
|
||||
@@ -91,6 +93,8 @@ public final class ClienteHandlers implements HttpHandler {
|
||||
private boolean podeAcessarCondominio(Connection c,String cargo,long fid,long cond)throws SQLException{return adminEmpresa(cargo)||count(c,"SELECT count(*) FROM funcionario_condominio WHERE funcionario_id=? AND condominio_id=?",fid,cond)>0;}
|
||||
private void detalheCondominio(HttpExchange ex,Connection c,long id)throws Exception{Map<String,Object> cond=one(c,"SELECT * FROM condominios WHERE id=?",Arrays.asList(id)); if(cond==null){HttpResponses.json(ex,404,Map.of("erro","condominio_nao_encontrado"));return;} Map<String,Object> sind=one(c,"SELECT cc.*, c.nome contato_nome,c.telefone contato_telefone,c.email contato_email FROM contato_condominio cc JOIN contatos c ON c.id=cc.contato_id WHERE cc.condominio_id=? AND cc.tipo='sindico' AND cc.status='ativo' ORDER BY cc.data_inicio_mandato DESC NULLS LAST LIMIT 1",Arrays.asList(id)); long ass=count(c,"SELECT count(*) FROM assembleias WHERE condominio_id=? AND data_hora>now() AND status='agendada'",id); long av=count(c,"SELECT count(*) FROM avisos WHERE condominio_id=? AND (data_expiracao IS NULL OR data_expiracao>now())",id); Map<String,Object> resp=new LinkedHashMap<>();resp.put("condominio",cond);resp.put("sindico_atual",sind);resp.put("assembleias_futuras",ass);resp.put("avisos_ativos",av);HttpResponses.json(ex,200,resp);}
|
||||
private void atualizarCondominio(HttpExchange ex,Connection c,long id,long fid)throws Exception{Map<String,String>b=body(ex); Map<String,Object> old=one(c,"SELECT * FROM condominios WHERE id=?",Arrays.asList(id)); if(old==null){HttpResponses.json(ex,404,Map.of("erro","condominio_nao_encontrado"));return;} try(PreparedStatement ps=c.prepareStatement("UPDATE condominios SET nome=COALESCE(?,nome), cnpj=COALESCE(?,cnpj), endereco=COALESCE(?,endereco) WHERE id=?")){ps.setString(1,b.get("nome"));ps.setString(2,b.get("cnpj"));ps.setString(3,b.get("endereco"));ps.setLong(4,id);ps.executeUpdate();} audit(c,fid,"update","condominios",id,old,b); detalheCondominio(ex,c,id);}
|
||||
private void listarRespostas(HttpExchange ex,Connection c,long cond)throws Exception{HttpResponses.json(ex,200,Map.of("respostas",rows(c,"SELECT r.*, co.nome contato_nome, co.telefone contato_telefone, co.email contato_email FROM respostas r LEFT JOIN contatos co ON co.id=r.contato_id WHERE r.condominio_id=? ORDER BY r.recebido_em DESC,id DESC",Arrays.asList(cond))));}
|
||||
private void registrarResposta(HttpExchange ex,Connection c,long fid,Long condPath)throws Exception{String raw=rawBody(ex);Map<String,String>b=SimpleJson.parseObject(raw);Long contato=optLong(firstNonBlank(b.get("contato_id"),b.get("destinatario_id")));Long cond=condPath!=null?condPath:optLong(b.get("condominio_id"));if(cond==null&&contato!=null){Map<String,Object> link=one(c,"SELECT condominio_id FROM contato_condominio WHERE contato_id=? AND status='ativo' ORDER BY CASE WHEN tipo='sindico' THEN 0 ELSE 1 END, condominio_id LIMIT 1",Arrays.asList(contato));if(link!=null)cond=((Number)link.get("condominio_id")).longValue();}if(cond==null){HttpResponses.json(ex,400,Map.of("erro","condominio_obrigatorio","mensagem","Informe condominio_id ou contato_id vinculado a um condominio."));return;}Map<String,Object> contatoRow=contato==null?null:one(c,"SELECT id,nome,telefone,email FROM contatos WHERE id=?",Arrays.asList(contato));String texto=firstNonBlank(b.get("resposta"),b.get("resposta_texto"),b.get("mensagem"),b.get("texto"),b.get("body"));if(texto.isBlank()){HttpResponses.json(ex,400,Map.of("erro","resposta_obrigatoria","mensagem","Informe a resposta recebida."));return;}long rid=insert(c,"INSERT INTO respostas (condominio_id,contato_id,mensagem_id,tipo_origem,nome_origem,telefone_origem,resposta_texto,mensagem_original,payload,origem,status) VALUES (?,?,?,?,?,?,?,?,?,?,?)",Arrays.asList(cond,contato,optLong(b.get("mensagem_id")),nullable(firstNonBlank(b.get("tipo"),b.get("tipo_origem"))),nullable(firstNonBlank(b.get("nome"),contatoRow==null?null:str(contatoRow.get("nome")))),nullable(firstNonBlank(b.get("telefone"),contatoRow==null?null:str(contatoRow.get("telefone")))),texto,nullable(firstNonBlank(b.get("mensagem_original"),b.get("pergunta"),b.get("mensagem_enviada"))),raw,firstNonBlank(b.get("origem"),"n8n"),firstNonBlank(b.get("status"),"recebida")));audit(c,fid,"create","respostas",rid,null,b);HttpResponses.json(ex,201,Map.of("ok",true,"resposta",one(c,"SELECT * FROM respostas WHERE id=?",Arrays.asList(rid)))) ;}
|
||||
private void criarContato(HttpExchange ex,Connection c,long fid)throws Exception{Map<String,String>b=body(ex); long id=insert(c,"INSERT INTO contatos (nome,telefone,email) VALUES (?,?,?)",Arrays.asList(req(b,"nome"),nullable(b.get("telefone")),nullable(b.get("email")))); audit(c,fid,"create","contatos",id,null,b); HttpResponses.json(ex,201,Map.of("contato",one(c,"SELECT * FROM contatos WHERE id=?",Arrays.asList(id))));}
|
||||
private void vincularContato(HttpExchange ex,Connection c,long cond,long fid)throws Exception{Map<String,String>b=body(ex); Long contato=optLong(b.get("contato_id")); if(contato==null){contato=insert(c,"INSERT INTO contatos (nome,telefone,email) VALUES (?,?,?)",Arrays.asList(req(b,"nome"),nullable(b.get("telefone")),nullable(b.get("email"))));} String tipo=req(b,"tipo"); if("sindico".equals(tipo)){try(PreparedStatement ps=c.prepareStatement("UPDATE contato_condominio SET status='inativo', data_fim_mandato=current_date WHERE condominio_id=? AND tipo='sindico' AND status='ativo' AND contato_id<>?")){ps.setLong(1,cond);ps.setLong(2,contato);ps.executeUpdate();}} try(PreparedStatement ps=c.prepareStatement("INSERT INTO contato_condominio (contato_id,condominio_id,tipo,data_inicio_mandato,data_fim_mandato,status) VALUES (?,?,?,?,?,'ativo') ON CONFLICT (contato_id,condominio_id,tipo) DO UPDATE SET data_inicio_mandato=EXCLUDED.data_inicio_mandato,data_fim_mandato=EXCLUDED.data_fim_mandato,status='ativo'")){ps.setLong(1,contato);ps.setLong(2,cond);ps.setString(3,tipo);setDate(ps,4,b.get("data_inicio_mandato"));setDate(ps,5,b.get("data_fim_mandato"));ps.executeUpdate();} if(Set.of("sindico","subsindico","conselheiro").contains(tipo))vincularContatoAosAvisosRecorrentes(c,cond,contato); audit(c,fid,"create","contato_condominio",contato,null,b); listarContatos(ex,c,cond);}
|
||||
private void vincularContatoAosAvisosRecorrentes(Connection c,long cond,long contato)throws SQLException{
|
||||
@@ -567,7 +571,7 @@ public final class ClienteHandlers implements HttpHandler {
|
||||
if(x.matches("\\d{2}/\\d{2}/\\d{4} \\d{2}:\\d{2}:\\d{2}")){LocalDateTime dt=LocalDateTime.parse(x,java.time.format.DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"));return Timestamp.valueOf(dt);}
|
||||
}catch(Exception ignored){}
|
||||
throw new IllegalArgumentException("Data/hora invalida. Use dd/mm/aaaa hh:mm.");
|
||||
} private int recorrenciaMeses(String v){if(v==null||v.isBlank())return 0;int n=Integer.parseInt(v);if(n==0||n==1||n==3||n==12)return n;throw new IllegalArgumentException("Recorrencia invalida. Use 0, 1, 3 ou 12 meses.");} private boolean bool(String v){return "true".equalsIgnoreCase(v)||"1".equals(v)||"sim".equalsIgnoreCase(v);} private String str(Object v){return v==null?"":String.valueOf(v);} private String cnpj(String v){String d=v==null?"":v.replaceAll("\\D","");if(d.length()!=14)throw new IllegalArgumentException("CNPJ invalido. Informe 14 digitos.");return d;}
|
||||
} private int recorrenciaMeses(String v){if(v==null||v.isBlank())return 0;int n=Integer.parseInt(v);if(n==0||n==1||n==3||n==12)return n;throw new IllegalArgumentException("Recorrencia invalida. Use 0, 1, 3 ou 12 meses.");} private boolean bool(String v){return "true".equalsIgnoreCase(v)||"1".equals(v)||"sim".equalsIgnoreCase(v);} private String str(Object v){return v==null?"":String.valueOf(v);} private String firstNonBlank(String...vals){for(String v:vals)if(v!=null&&!v.isBlank())return v;return "";} private String cnpj(String v){String d=v==null?"":v.replaceAll("\\D","");if(d.length()!=14)throw new IllegalArgumentException("CNPJ invalido. Informe 14 digitos.");return d;}
|
||||
private List<Long> longArray(String raw,String key){String a=arrayBody(raw,key);if(a.isBlank())return List.of();List<Long> out=new ArrayList<>();for(String x:a.split(",")){String v=x.trim().replace("\"","");if(!v.isBlank())out.add(Long.parseLong(v));}return out;}
|
||||
private List<Destinatario> destinatarios(String raw){String a=arrayBody(raw,"destinatarios");if(a.isBlank())return List.of();List<Destinatario> out=new ArrayList<>();for(String obj:a.split("\\},\\s*\\{")){String x=obj.replace("{","").replace("}","");String tipo=null;Long id=null;for(String kv:x.split(",")){String[]p=kv.split(":",2);if(p.length<2)continue;String k=p[0].trim().replace("\"","");String v=p[1].trim().replace("\"","");if("tipo".equals(k)||"tipo_destinatario".equals(k))tipo=v;if("id".equals(k)||"destinatario_id".equals(k))id=Long.parseLong(v);}if(tipo!=null&&id!=null)out.add(new Destinatario(tipo,id));}return out;}
|
||||
private String arrayBody(String raw,String key){String marker="\""+key+"\"";int p=raw.indexOf(marker);if(p<0)return"";int s=raw.indexOf('[',p);if(s<0)return"";int depth=0;for(int i=s;i<raw.length();i++){char ch=raw.charAt(i);if(ch=='[')depth++;if(ch==']'&&--depth==0)return raw.substring(s+1,i).trim();}return"";}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE respostas (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
condominio_id BIGINT NOT NULL REFERENCES condominios(id),
|
||||
contato_id BIGINT REFERENCES contatos(id),
|
||||
mensagem_id BIGINT,
|
||||
tipo_origem VARCHAR(40),
|
||||
nome_origem VARCHAR(160),
|
||||
telefone_origem VARCHAR(40),
|
||||
resposta_texto TEXT NOT NULL,
|
||||
mensagem_original TEXT,
|
||||
payload TEXT,
|
||||
origem VARCHAR(80) NOT NULL DEFAULT 'n8n',
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'recebida',
|
||||
recebido_em TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_respostas_condominio ON respostas(condominio_id, recebido_em DESC);
|
||||
CREATE INDEX idx_respostas_contato ON respostas(contato_id, recebido_em DESC);
|
||||
CREATE INDEX idx_respostas_mensagem ON respostas(mensagem_id);
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Detalhe do Condominio</title><link rel="stylesheet" href="/css/app.css"><script src="/js/auth.js"></script><script src="/js/util.js"></script><script src="/js/api.js"></script><script src="/js/layout.js"></script></head><body><main><div class="topbar"><h1>Detalhe do Condominio</h1></div><section class="panel" id="header"></section><div class="nav-tabs"><button data-tab="contatos" class="active">Contatos</button><button data-tab="espacos">Espacos</button><button data-tab="assembleias">Assembleias</button><button data-tab="documentos">Documentos</button><button data-tab="avisos">Avisos</button></div><section class="panel tab" id="contatos"></section><section class="panel tab hidden" id="espacos"></section><section class="panel tab hidden" id="assembleias"></section><section class="panel tab hidden" id="documentos"></section><section class="panel tab hidden" id="avisos"></section></main><script>Layout.mount('empresa','condominios');</script><script src="/cliente/condominio-detalhe.js"></script></body></html>
|
||||
<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Detalhe do Condominio</title><link rel="stylesheet" href="/css/app.css"><script src="/js/auth.js"></script><script src="/js/util.js"></script><script src="/js/api.js"></script><script src="/js/layout.js"></script></head><body><main><div class="topbar"><h1>Detalhe do Condominio</h1></div><section class="panel" id="header"></section><div class="nav-tabs"><button data-tab="contatos" class="active">Contatos</button><button data-tab="espacos">Espacos</button><button data-tab="assembleias">Assembleias</button><button data-tab="documentos">Documentos</button><button data-tab="avisos">Avisos</button><button data-tab="respostas">Respostas</button></div><section class="panel tab" id="contatos"></section><section class="panel tab hidden" id="espacos"></section><section class="panel tab hidden" id="assembleias"></section><section class="panel tab hidden" id="documentos"></section><section class="panel tab hidden" id="avisos"></section><section class="panel tab hidden" id="respostas"></section></main><script>Layout.mount('empresa','condominios');</script><script src="/cliente/condominio-detalhe.js"></script></body></html>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const id=new URLSearchParams(location.search).get('id');const sections=['contatos','espacos','assembleias','documentos','avisos'];document.querySelectorAll('.nav-tabs button').forEach(b=>b.onclick=()=>{document.querySelectorAll('.nav-tabs button').forEach(x=>x.classList.remove('active'));b.classList.add('active');sections.forEach(s=>document.querySelector('#'+s).classList.toggle('hidden',s!==b.dataset.tab))});let contatos=[],docs=[],avisos=[],funcionariosVinculados=[],extras=[],assembleiasCalendario=[],calMonth=new Date(),espacos=[],assembleias=[];function recLabel(v){return {1:'Mensal',3:'Trimestral',12:'Anual'}[Number(v)]||'Sem recorrencia'}
|
||||
const id=new URLSearchParams(location.search).get('id');const sections=['contatos','espacos','assembleias','documentos','avisos','respostas'];document.querySelectorAll('.nav-tabs button').forEach(b=>b.onclick=()=>{document.querySelectorAll('.nav-tabs button').forEach(x=>x.classList.remove('active'));b.classList.add('active');sections.forEach(s=>document.querySelector('#'+s).classList.toggle('hidden',s!==b.dataset.tab))});let contatos=[],docs=[],avisos=[],respostas=[],funcionariosVinculados=[],extras=[],assembleiasCalendario=[],calMonth=new Date(),espacos=[],assembleias=[];function recLabel(v){return {1:'Mensal',3:'Trimestral',12:'Anual'}[Number(v)]||'Sem recorrencia'}
|
||||
function esc(v){return String(v??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]))}
|
||||
function lupaBtn(fn,label){return `<button class="search-icon-btn" type="button" onclick="${fn}" aria-label="${label}" title="Buscar"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"></circle><path d="m16.5 16.5 4 4"></path></svg></button>`}
|
||||
function headerCadastro(titulo,tipo,novoFn,extra=''){const q=esc(termoDetalhe(tipo)),open=document.querySelector(`#${tipo}-search`)&&!document.querySelector(`#${tipo}-search`)?.classList.contains('hidden');return `<div class="section-head"><h2>${titulo}</h2><div class="section-actions">${extra}${lupaBtn(`alternarBuscaDetalhe('${tipo}')`,'Buscar '+titulo)}<button class="btn" type="button" onclick="${novoFn}">Novo</button></div></div><div id="${tipo}-search" class="search-strip ${open?'':'hidden'}"><input id="${tipo}-search-input" placeholder="Buscar" value="${q}"></div>`}
|
||||
function headerConsulta(titulo,tipo,extra=''){const q=esc(termoDetalhe(tipo)),open=document.querySelector(`#${tipo}-search`)&&!document.querySelector(`#${tipo}-search`)?.classList.contains('hidden');return `<div class="section-head"><h2>${titulo}</h2><div class="section-actions">${extra}${lupaBtn(`alternarBuscaDetalhe('${tipo}')`,'Buscar '+titulo)}</div></div><div id="${tipo}-search" class="search-strip ${open?'':'hidden'}"><input id="${tipo}-search-input" placeholder="Buscar" value="${q}"></div>`}
|
||||
function termoDetalhe(tipo){return document.querySelector(`#${tipo}-search-input`)?.value.trim().toLowerCase()||''}
|
||||
function contemDetalhe(q,...vals){return !q||vals.some(v=>String(v??'').toLowerCase().includes(q))}
|
||||
window.alternarBuscaDetalhe=tipo=>{const strip=document.querySelector(`#${tipo}-search`),input=document.querySelector(`#${tipo}-search-input`);if(!strip||!input)return;strip.classList.toggle('hidden');if(!strip.classList.contains('hidden'))input.focus()};
|
||||
@@ -26,7 +27,7 @@ window.abrirAvisoNotificacao=()=>{const m=ensureAvisoNotifModal(),ids=new Set(av
|
||||
window.fecharAvisoNotificacao=()=>document.querySelector('#aviso-notif-modal')?.classList.add('hidden');
|
||||
window.salvarAvisoNotificacao=()=>{const ids=new Set([...document.querySelectorAll('#aviso-notif-body input:checked')].map(x=>x.value));avisoDestInputs().forEach(x=>x.checked=ids.has(x.value));updateAvisoNotifSummary();fecharAvisoNotificacao()};
|
||||
async function safeLoad(section,fn){try{await fn()}catch(e){document.querySelector('#'+section).innerHTML=`<p>${e.message}</p>`;Util.toast(e.message,true)}}
|
||||
async function load(){try{const d=await Api.get(`/api/condominios/${id}`);document.querySelector('#header').innerHTML=`<h2>${d.condominio.nome}</h2><p>${d.condominio.endereco||''}</p><div class="cards"><div class="stat">Sindico<strong>${d.sindico_atual?.contato_nome||'-'}</strong></div><div class="stat">Assembleias futuras<strong>${d.assembleias_futuras||0}</strong></div><div class="stat">Avisos ativos<strong>${d.avisos_ativos||0}</strong></div></div>`;await safeLoad('contatos',loadContatos);await safeLoad('avisos',loadFuncionariosVinculados);await safeLoad('avisos',loadExtras);await safeLoad('espacos',loadEspacos);await safeLoad('assembleias',loadAssembleias);await safeLoad('documentos',loadDocs);await safeLoad('avisos',loadAvisos)}catch(e){Util.toast(e.message,true)}}
|
||||
async function load(){try{const d=await Api.get(`/api/condominios/${id}`);document.querySelector('#header').innerHTML=`<h2>${d.condominio.nome}</h2><p>${d.condominio.endereco||''}</p><div class="cards"><div class="stat">Sindico<strong>${d.sindico_atual?.contato_nome||'-'}</strong></div><div class="stat">Assembleias futuras<strong>${d.assembleias_futuras||0}</strong></div><div class="stat">Avisos ativos<strong>${d.avisos_ativos||0}</strong></div></div>`;await safeLoad('contatos',loadContatos);await safeLoad('avisos',loadFuncionariosVinculados);await safeLoad('avisos',loadExtras);await safeLoad('espacos',loadEspacos);await safeLoad('assembleias',loadAssembleias);await safeLoad('documentos',loadDocs);await safeLoad('avisos',loadAvisos);await safeLoad('respostas',loadRespostas)}catch(e){Util.toast(e.message,true)}}
|
||||
async function loadContatos(){const d=await Api.get(`/api/condominios/${id}/contatos`);contatos=d.contatos||[];renderContatos()}
|
||||
function renderContatos(){const q=termoDetalhe('contatos');const lista=contatos.filter(c=>contemDetalhe(q,c.contato_nome,c.tipo,c.contato_telefone,c.contato_email));document.querySelector('#contatos').innerHTML=headerCadastro('Contatos','contatos','abrirNovoContato()')+`<div class="table-wrap"><table><thead><tr><th>Nome</th><th>Tipo</th><th>Telefone</th><th>Email</th></tr></thead><tbody>${lista.map(c=>`<tr><td>${esc(c.contato_nome)}</td><td>${esc(c.tipo)}</td><td>${esc(c.contato_telefone||'')}</td><td>${esc(c.contato_email||'')}</td></tr>`).join('')||'<tr><td colspan="4">Nenhum contato encontrado.</td></tr>'}</tbody></table></div>`;document.querySelector('#contatos-search-input')?.addEventListener('input',renderContatos)}
|
||||
window.abrirNovoContato=()=>{abrirDetailModal('Novo contato','<form id="contato-form" class="grid cols"><div class="field"><label>Nome</label><input name="nome" required></div><div class="field"><label>Telefone</label><input name="telefone"></div><div class="field"><label>E-mail</label><input name="email"></div><div class="field"><label>Tipo</label><select name="tipo"><option value="sindico">Sindico</option><option value="subsindico">Subsindico</option><option value="conselheiro">Conselheiro</option></select></div><div class="row"><button class="btn">Cadastrar</button><button class="btn secondary" type="button" onclick="fecharDetailModal()">Cancelar</button></div></form>');document.querySelector('#contato-form').onsubmit=async e=>{e.preventDefault();try{await Api.post(`/api/condominios/${id}/contatos/vincular`,Util.formData(e.target));Util.toast('Contato salvo');fecharDetailModal();loadContatos();load()}catch(err){Util.toast(err.message,true)}}};
|
||||
@@ -48,6 +49,8 @@ function avisoFormHtml(){const destContatos=contatos.map(c=>`<label><input type=
|
||||
async function loadAvisos(){const d=await Api.get(`/api/condominios/${id}/avisos`);avisos=d.avisos||[];renderAvisos()}
|
||||
function renderAvisos(){const q=termoDetalhe('avisos');const lista=avisos.filter(a=>contemDetalhe(q,a.titulo,a.status_envio,a.data_evento,a.data_publicacao,recLabel(a.recorrencia_meses)));document.querySelector('#avisos').innerHTML=headerCadastro('Avisos','avisos','abrirNovoAviso()')+`<div class="table-wrap"><table><thead><tr><th>Titulo</th><th>Status</th><th>Envio</th><th>Publicado em</th><th>Recorrencia</th><th>Acoes</th></tr></thead><tbody>${lista.map(a=>`<tr><td>${esc(a.titulo)}</td><td>${Util.badge(a.status_envio)}</td><td>${Util.date(a.data_evento)}</td><td>${Util.date(a.data_publicacao)}</td><td>${recLabel(a.recorrencia_meses)}</td><td><button class="btn secondary" onclick="reenviarAviso(${a.id})">Reenviar</button> <button class="btn danger" onclick="excluirAviso(${a.id})">Excluir</button></td></tr>`).join('')||'<tr><td colspan="6">Nenhum aviso encontrado.</td></tr>'}</tbody></table></div>`;document.querySelector('#avisos-search-input')?.addEventListener('input',renderAvisos)}
|
||||
window.abrirNovoAviso=()=>{abrirDetailModal('Novo aviso',avisoFormHtml());updateAvisoNotifSummary();document.querySelector('#aviso-form').onsubmit=async e=>{e.preventDefault();const f=e.target,b=Util.formData(f);b.enviar_whatsapp=!!f.enviar_whatsapp.checked;b.enviar_email=!!f.enviar_email.checked;b.destinatarios=[...f.querySelectorAll('input[name=dest]:checked')].map(x=>{const [tipo,did]=x.value.split(':');return{tipo_destinatario:tipo,id:Number(did)}});b.documentos=[...f.querySelectorAll('input[name=doc]:checked')].map(x=>Number(x.value));try{await Api.post(`/api/condominios/${id}/avisos`,b);Util.toast('Aviso criado');fecharDetailModal();loadAvisos();load()}catch(err){Util.toast(err.message,true)}}};
|
||||
async function loadRespostas(){const d=await Api.get(`/api/condominios/${id}/respostas`);respostas=d.respostas||[];renderRespostas()}
|
||||
function renderRespostas(){const q=termoDetalhe('respostas');const lista=respostas.filter(r=>contemDetalhe(q,r.resposta_texto,r.mensagem_original,r.nome_origem,r.telefone_origem,r.contato_nome,r.mensagem_id,r.origem,r.status,r.tipo_origem));document.querySelector('#respostas').innerHTML=headerConsulta('Respostas','respostas')+`<div class="table-wrap"><table><thead><tr><th>Recebido em</th><th>Contato</th><th>Telefone</th><th>Mensagem ID</th><th>Resposta</th><th>Origem</th><th>Status</th></tr></thead><tbody>${lista.map(r=>`<tr><td>${Util.date(r.recebido_em)}</td><td><strong>${esc(r.contato_nome||r.nome_origem||'-')}</strong><br><span class="muted">${esc(r.tipo_origem||'')}</span></td><td>${esc(r.contato_telefone||r.telefone_origem||'')}</td><td>${esc(r.mensagem_id||'')}</td><td>${esc(r.resposta_texto||'')}<br><span class="muted">${esc(r.mensagem_original||'')}</span></td><td>${esc(r.origem||'')}</td><td>${Util.badge(r.status)}</td></tr>`).join('')||'<tr><td colspan="7">Nenhuma resposta registrada.</td></tr>'}</tbody></table></div>`;document.querySelector('#respostas-search-input')?.addEventListener('input',renderRespostas)}
|
||||
window.reenviarAviso=async avisoId=>{try{await Api.post(`/api/condominios/${id}/avisos/${avisoId}/reenviar`,{});Util.toast('Aviso reenviado');loadAvisos()}catch(err){Util.toast(err.message,true)}};
|
||||
window.excluirAviso=async avisoId=>{try{await Api.request(`/api/condominios/${id}/avisos/${avisoId}`,{method:'DELETE'});Util.toast('Aviso excluido');loadAvisos();load()}catch(err){Util.toast(err.message,true)}};
|
||||
load();
|
||||
|
||||
Reference in New Issue
Block a user