diff --git a/src/main/webapp/cliente/assembleias.js b/src/main/webapp/cliente/assembleias.js
index 0a85bac..37fcef9 100644
--- a/src/main/webapp/cliente/assembleias.js
+++ b/src/main/webapp/cliente/assembleias.js
@@ -12,7 +12,7 @@ function dateKey(v){const d=new Date(v);if(Number.isNaN(d.getTime()))return'';co
function monthLabel(d){return d.toLocaleDateString('pt-BR',{month:'long',year:'numeric'})}
function formHtml(r={}){
const dias=new Set(String(r.dias_semana||'').split(','));
- return `
`;
+ return ``;
}
const Icons = {
diff --git a/src/main/webapp/cliente/condominio-detalhe.js b/src/main/webapp/cliente/condominio-detalhe.js
index f942b06..dc5aea6 100644
--- a/src/main/webapp/cliente/condominio-detalhe.js
+++ b/src/main/webapp/cliente/condominio-detalhe.js
@@ -28,7 +28,7 @@ window.mudarMesCalendario=delta=>{calMonth=new Date(calMonth.getFullYear(),calMo
window.selecionarDiaCalendario=(key,mark=true)=>{if(mark)document.querySelectorAll('.calendar-day').forEach(x=>x.classList.toggle('selected',x.dataset.date===key));const list=document.querySelector('#calendar-day-list'),title=document.querySelector('#calendar-day-title'),items=assembleiasCalendario.filter(a=>dateKey(a.data_hora)===key);const [yy,mm,dd]=key.split('-');title.textContent=dd+'/'+mm+'/'+yy;if(!items.length){list.innerHTML='Nenhuma assembleia agendada neste dia.
';return;}list.innerHTML=items.map(a=>{const dest=(a.destinatarios||[]).map(x=>x.nome+' ('+x.tipo+')').join(', ')||'Nenhum destinatário registrado';return ''+a.condominio_nome+''+Util.date(a.data_hora)+''+a.tipo+' - '+(a.local||'-')+''+dest+'
'}).join('')};
window.reenviarAvisoAssembleia=async(cond,aid)=>{try{const res=await Api.post('/api/condominios/'+cond+'/assembleias/'+aid+'/notificar',{});if(res&&res.ok===false){Util.toast(res.mensagem||'Falha ao enviar notificação.',true);}else{Util.toast(res?.mensagem||'Notificação enviada com sucesso!');}await abrirCalendarioAssembleias();await loadAssembleias();await load();}catch(e){Util.toast(e.message,true)}};
window.cancelarAssembleiaCalendario=async(cond,aid)=>{if(!await Util.confirm('Cancelar esta assembleia?','Cancelar assembleia'))return;try{await Api.put('/api/condominios/'+cond+'/assembleias/'+aid,{status:'cancelada'});Util.toast('Assembleia cancelada');await abrirCalendarioAssembleias();await loadAssembleias();await load()}catch(e){Util.toast(e.message,true)}};
-function ensureAvisoNotifModal(){let m=document.querySelector('#aviso-notif-modal');if(m)return m;document.body.insertAdjacentHTML('beforeend','Notificação
');return document.querySelector('#aviso-notif-modal')}
+function ensureAvisoNotifModal(){let m=document.querySelector('#aviso-notif-modal');if(m)return m;document.body.insertAdjacentHTML('beforeend','Notificação
');return document.querySelector('#aviso-notif-modal')}
function avisoDestInputs(){return [...document.querySelectorAll('#aviso-dests-hidden input[name=dest]')]}
function updateAvisoNotifSummary(){const n=avisoDestInputs().filter(x=>x.checked).length;const el=document.querySelector('#aviso-notif-summary');if(el)el.textContent=n?(n+' selecionado'+(n>1?'s':'')):'Nenhuma pessoa selecionada'}
window.abrirAvisoNotificacao=()=>{const m=ensureAvisoNotifModal(),ids=new Set(avisoDestInputs().filter(x=>x.checked).map(x=>x.value));const pessoas=[...contatos.map(c=>({value:'contato:'+c.contato_id,nome:c.contato_nome,tipo:c.tipo})),...funcionariosVinculados.map(f=>({value:'funcionario:'+(f.funcionario_id||f.id),nome:f.nome,tipo:'funcionario'})),...extras.map(x=>({value:'extra:'+x.id,nome:x.nome,tipo:'extra'}))];document.querySelector('#aviso-notif-body').innerHTML=pessoas.map(p=>'').join('')||'Nenhuma pessoa cadastrada.
';m.classList.remove('hidden')};
@@ -44,7 +44,7 @@ function renderContatos(){
document.querySelector('#contatos-search-input')?.addEventListener('input',renderContatos);
}
window.desvincularContatoDetalhe=async (cid,tipo)=>{if(!await Util.confirm('Deseja desvincular este contato do condomínio?','Desvincular contato'))return;try{await Api.request(`/api/condominios/${id}/contatos/${cid}?tipo=${encodeURIComponent(tipo)}`,{method:'DELETE'});Util.toast('Contato desvinculado');loadContatos();load()}catch(e){Util.toast(e.message,true)}};
-window.abrirNovoContato=()=>{abrirDetailModal('Novo contato','');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)}}};
+window.abrirNovoContato=()=>{abrirDetailModal('Novo contato','');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)}}};
async function loadEspacos(){const d=await Api.get(`/api/condominios/${id}/espacos-comuns`);espacos=d.espacos||[];renderEspacos()}
function renderEspacos(){
@@ -53,9 +53,9 @@ function renderEspacos(){
document.querySelector('#espacos').innerHTML=headerCadastro('Espaços comuns','espacos','abrirNovoEspaco()')+`| Nome | Valor | Índice | Descrição | Ações |
${lista.map(x=>`| ${esc(x.nome)} | ${Util.money(x.valor_utilizacao_centavos)} | ${esc(x.indice_correcao||'')} | ${esc(x.descricao||'')} | |
`).join('')||'| Nenhum espaço encontrado. |
'}
`;
document.querySelector('#espacos-search-input')?.addEventListener('input',renderEspacos);
}
-window.abrirEditarEspaco=eid=>{const x=espacos.find(e=>Number(e.id)===Number(eid));if(!x)return;abrirDetailModal('Editar espaço comum','');document.querySelector('#espaco-edit-form').onsubmit=async e=>{e.preventDefault();try{await Api.put(`/api/condominios/${id}/espacos-comuns/${eid}`,Util.formData(e.target));Util.toast('Espaço atualizado');fecharDetailModal();loadEspacos()}catch(err){Util.toast(err.message,true)}}};
+window.abrirEditarEspaco=eid=>{const x=espacos.find(e=>Number(e.id)===Number(eid));if(!x)return;abrirDetailModal('Editar espaço comum','');document.querySelector('#espaco-edit-form').onsubmit=async e=>{e.preventDefault();try{await Api.put(`/api/condominios/${id}/espacos-comuns/${eid}`,Util.formData(e.target));Util.toast('Espaço atualizado');fecharDetailModal();loadEspacos()}catch(err){Util.toast(err.message,true)}}};
window.excluirEspacoDetalhe=async eid=>{if(!await Util.confirm('Tem certeza que deseja excluir este espaço comum?','Excluir espaço'))return;try{await Api.request(`/api/condominios/${id}/espacos-comuns/${eid}`,{method:'DELETE'});Util.toast('Espaço excluído');loadEspacos()}catch(err){Util.toast(err.message,true)}};
-window.abrirNovoEspaco=()=>{abrirDetailModal('Novo espaço comum','');document.querySelector('#espaco-form').onsubmit=async e=>{e.preventDefault();try{await Api.post(`/api/condominios/${id}/espacos-comuns`,Util.formData(e.target));Util.toast('Espaço salvo');fecharDetailModal();loadEspacos()}catch(err){Util.toast(err.message,true)}}};
+window.abrirNovoEspaco=()=>{abrirDetailModal('Novo espaço comum','');document.querySelector('#espaco-form').onsubmit=async e=>{e.preventDefault();try{await Api.post(`/api/condominios/${id}/espacos-comuns`,Util.formData(e.target));Util.toast('Espaço salvo');fecharDetailModal();loadEspacos()}catch(err){Util.toast(err.message,true)}}};
async function loadAssembleias(){const d=await Api.get(`/api/condominios/${id}/assembleias`);assembleias=d.assembleias||[];renderAssembleias()}
function renderAssembleias(){
@@ -66,7 +66,7 @@ function renderAssembleias(){
document.querySelector('#assembleias-search-input')?.addEventListener('input',renderAssembleias);
}
window.excluirAssembleiaDetalhe=async aid=>{if(!await Util.confirm('Tem certeza que deseja excluir esta assembleia? Esta ação não pode ser desfeita.','Excluir assembleia'))return;try{await Api.request(`/api/condominios/${id}/assembleias/${aid}`,{method:'DELETE'});Util.toast('Assembleia excluída');await loadAssembleias();await load()}catch(err){Util.toast(err.message,true)}};
-window.abrirNovaAssembleia=()=>{abrirDetailModal('Nova assembleia','');document.querySelector('#ass-form').onsubmit=async e=>{e.preventDefault();try{await Api.post(`/api/condominios/${id}/assembleias`,Util.formData(e.target));Util.toast('Assembleia salva');fecharDetailModal();loadAssembleias();load()}catch(err){Util.toast(err.message,true)}}};
+window.abrirNovaAssembleia=()=>{abrirDetailModal('Nova assembleia','');document.querySelector('#ass-form').onsubmit=async e=>{e.preventDefault();try{await Api.post(`/api/condominios/${id}/assembleias`,Util.formData(e.target));Util.toast('Assembleia salva');fecharDetailModal();loadAssembleias();load()}catch(err){Util.toast(err.message,true)}}};
async function loadFuncionariosVinculados(){const d=await Api.get(`/api/condominios/${id}/funcionarios-vinculados`);funcionariosVinculados=d.funcionarios||[]}
async function loadExtras(){const d=await Api.get(`/api/extras`);extras=(d.extras||[]).filter(x=>x.status==='ativo')}
async function loadDocs(){const d=await Api.get(`/api/condominios/${id}/documentos`);docs=d.documentos||[];renderDocs()}
@@ -77,9 +77,9 @@ function renderDocs(){
document.querySelector('#documentos-search-input')?.addEventListener('input',renderDocs);
}
window.excluirDocumentoDetalhe=async did=>{if(!await Util.confirm('Tem certeza que deseja excluir este documento?','Excluir documento'))return;try{await Api.request(`/api/condominios/${id}/documentos/${did}`,{method:'DELETE'});Util.toast('Documento excluído');loadDocs()}catch(err){Util.toast(err.message,true)}};
-window.abrirNovoDocumento=()=>{abrirDetailModal('Novo documento','');document.querySelector('#doc-form').onsubmit=async e=>{e.preventDefault();const fd=new FormData(e.target);try{await Api.post(`/api/condominios/${id}/documentos`,fd);Util.toast('Documento enviado');fecharDetailModal();loadDocs();loadAvisos()}catch(err){Util.toast(err.message,true)}}};
+window.abrirNovoDocumento=()=>{abrirDetailModal('Novo documento','');document.querySelector('#doc-form').onsubmit=async e=>{e.preventDefault();const fd=new FormData(e.target);try{await Api.post(`/api/condominios/${id}/documentos`,fd);Util.toast('Documento enviado');fecharDetailModal();loadDocs();loadAvisos()}catch(err){Util.toast(err.message,true)}}};
-function avisoFormHtml(){const destContatos=contatos.map(c=>``).join('');const destFuncionarios=funcionariosVinculados.map(f=>``).join('');const destExtras=extras.map(x=>``).join('');const dest=[destContatos,destFuncionarios,destExtras].filter(Boolean).join('');const docChecks=docs.map(d=>``).join('');return ``}
+function avisoFormHtml(){const destContatos=contatos.map(c=>``).join('');const destFuncionarios=funcionariosVinculados.map(f=>``).join('');const destExtras=extras.map(x=>``).join('');const dest=[destContatos,destFuncionarios,destExtras].filter(Boolean).join('');const docChecks=docs.map(d=>``).join('');return ``}
async function loadAvisos(){const d=await Api.get(`/api/condominios/${id}/avisos`);avisos=d.avisos||[];renderAvisos()}
function renderAvisos(){
const q=termoDetalhe('avisos');
diff --git a/src/main/webapp/cliente/condominios.js b/src/main/webapp/cliente/condominios.js
index 6007a0e..49dad62 100644
--- a/src/main/webapp/cliente/condominios.js
+++ b/src/main/webapp/cliente/condominios.js
@@ -5,7 +5,7 @@ function esc(v){return String(v??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'&l
function termo(id){return document.querySelector(id)?.value.trim().toLowerCase()||''}
function contem(item,q,campos){return !q||campos.some(c=>String(item[c]??'').toLowerCase().includes(q))}
function abrirModal(titulo,html){modalTitle.textContent=titulo;modalBody.innerHTML=html;modal.classList.remove('hidden')}
-function formActions(label){return ''}
+function formActions(label){return ``}
const Icons = {
eye: '',
@@ -96,10 +96,9 @@ window.abrirNovoFuncionario=()=>{
function funcionarioPorId(funcId){return funcionarios.find(x=>Number(x.id)===Number(funcId))}
window.abrirEditarFuncionario=funcId=>{const f=funcionarioPorId(funcId);if(!f)return;abrirModal('Editar funcionario','');document.querySelector('#func-edit-form').onsubmit=async e=>{e.preventDefault();try{await Api.put('/api/funcionarios/'+funcId,Util.formData(e.target));Util.toast('Funcionario atualizado');fecharModalCondominios();loadFuncionarios()}catch(err){Util.toast(err.message,true)}}};
-window.abrirExcluirFuncionario=funcId=>{const f=funcionarioPorId(funcId);if(!f)return;abrirModal('Excluir funcionario','Deseja realmente excluir o acesso de '+esc(f.nome||'')+'?
O funcionario sera inativado e perdera acesso ao sistema.
')};
-window.confirmarExcluirFuncionario=async funcId=>{try{await Api.request('/api/funcionarios/'+funcId,{method:'DELETE'});Util.toast('Funcionario inativado');fecharModalCondominios();loadFuncionarios()}catch(err){Util.toast(err.message,true)}};
+window.abrirExcluirFuncionario=async funcId=>{const f=funcionarioPorId(funcId);if(!f)return;if(!await Util.confirm('Deseja realmente excluir o acesso de "'+esc(f.nome||'')+'"? O funcionário será inativado e perderá o acesso ao sistema.','Excluir funcionário'))return;try{await Api.request('/api/funcionarios/'+funcId,{method:'DELETE'});Util.toast('Funcionário inativado');loadFuncionarios()}catch(err){Util.toast(err.message,true)}};
-function renderModalCondominios(){const f=funcionarios.find(x=>Number(x.id)===Number(funcAtual));if(!f)return;const vinculados=funcLinks.get(Number(funcAtual))||[];const vinculadosIds=new Set(vinculados.map(c=>Number(c.id)));const disponiveis=condominios.filter(c=>!vinculadosIds.has(Number(c.id)));modalTitle.textContent=`Condominios de ${f.nome}`;const listaVinculados=vinculados.map(c=>`${esc(c.nome)}
`).join('')||'Nenhum condominio vinculado.
';const listaDisponiveis=disponiveis.map(c=>`${esc(c.nome)}
`).join('')||'Todos os condominios ja estao vinculados.
';modalBody.innerHTML=`Vinculados
${listaVinculados}Nao vinculados
${listaDisponiveis}
`}
+function renderModalCondominios(){const f=funcionarios.find(x=>Number(x.id)===Number(funcAtual));if(!f)return;const vinculados=funcLinks.get(Number(funcAtual))||[];const vinculadosIds=new Set(vinculados.map(c=>Number(c.id)));const disponiveis=condominios.filter(c=>!vinculadosIds.has(Number(c.id)));modalTitle.textContent=`Condomínios de ${f.nome}`;const listaVinculados=vinculados.map(c=>`${esc(c.nome)}
`).join('')||'Nenhum condomínio vinculado.
';const listaDisponiveis=disponiveis.map(c=>`${esc(c.nome)}
`).join('')||'Todos os condomínios já estão vinculados.
';modalBody.innerHTML=`Vinculados
${listaVinculados}Não vinculados
${listaDisponiveis}
`}
window.abrirModalCondominios=async funcId=>{funcAtual=Number(funcId);try{await refreshLinks();renderModalCondominios();modal.classList.remove('hidden')}catch(err){Util.toast(err.message,true)}};
window.fecharModalCondominios=()=>{modal.classList.add('hidden');funcAtual=null;modalBody.innerHTML=''};
window.vincularFuncionarioCondominio=async (funcId,condId)=>{try{await Api.post(`/api/condominios/${condId}/funcionarios-vinculados`,{funcionario_id:funcId});Util.toast('Funcionario vinculado');await refreshLinks();renderModalCondominios()}catch(err){Util.toast(err.message,true)}};
diff --git a/src/main/webapp/cliente/lembretes.js b/src/main/webapp/cliente/lembretes.js
index c07b0a5..8c6a104 100644
--- a/src/main/webapp/cliente/lembretes.js
+++ b/src/main/webapp/cliente/lembretes.js
@@ -9,7 +9,7 @@ function selectedDestIds(){return form?new Set([...form.querySelectorAll('input[
function updateNotifSummary(){const n=selectedDestIds().size;const el=document.querySelector('#notif-summary');if(el)el.textContent=n?(n+' selecionado'+(n>1?'s':'')):'Nenhuma pessoa selecionada'}
function formHtml(){
- return '';
+ return '';
}
function bindForm(){
@@ -90,7 +90,7 @@ window.alternarBuscaLembretes=()=>{
if(!strip.classList.contains('hidden'))input.focus();
};
-function ensureNotifModal(){let m=document.querySelector('#notif-modal');if(m)return m;document.body.insertAdjacentHTML('beforeend','Notificação
');return document.querySelector('#notif-modal')}
+function ensureNotifModal(){let m=document.querySelector('#notif-modal');if(m)return m;document.body.insertAdjacentHTML('beforeend','Notificação
');return document.querySelector('#notif-modal')}
window.abrirNotificacao=()=>{if(!form)return;const m=ensureNotifModal(),ids=selectedDestIds();document.querySelector('#notif-modal-body').innerHTML=pessoas.map(p=>'').join('')||'Nenhum funcionario ativo.
';m.classList.remove('hidden')};
window.fecharNotificacao=()=>document.querySelector('#notif-modal')?.classList.add('hidden');
window.salvarNotificacao=()=>{if(!form)return;const ids=new Set([...document.querySelectorAll('#notif-modal-body input:checked')].map(x=>String(x.value)));form.querySelectorAll('input[name=dest]').forEach(x=>x.checked=ids.has(String(x.value)));updateNotifSummary();fecharNotificacao()};
diff --git a/src/main/webapp/css/app.css b/src/main/webapp/css/app.css
index 0c1c462..228bf12 100644
--- a/src/main/webapp/css/app.css
+++ b/src/main/webapp/css/app.css
@@ -853,19 +853,44 @@ th {
}
.modal-actions {
+ grid-column: 1 / -1;
+ width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 12px;
flex-wrap: wrap;
- padding-top: 16px;
+ padding-top: 18px;
border-top: 1px solid var(--line);
+ margin-top: 14px;
}
.modal-actions .btn {
min-width: 120px;
}
+.modal-panel form.grid.cols,
+.modal-panel form.grid {
+ align-items: start;
+}
+
+.modal-panel form > .row:last-child {
+ grid-column: 1 / -1;
+ width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 12px;
+ flex-wrap: wrap;
+ padding-top: 18px;
+ border-top: 1px solid var(--line);
+ margin-top: 14px;
+}
+
+.modal-panel form > .row:last-child .btn {
+ min-width: 120px;
+}
+
.confirm-panel {
width: min(460px, 100%);
}