Unifica tela de lembretes
This commit is contained in:
@@ -1 +1 @@
|
||||
<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Lembrete</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>Lembrete</h1></div><section class="panel"><h2>Novo lembrete</h2><form id="lembrete-form" class="grid cols"><input type="hidden" name="id"><div class="field"><label>Titulo</label><input name="titulo" required></div><div class="field"><label>Data/hora</label><input name="data_hora" type="datetime-local"></div><div class="field"><label>Status</label><select name="status"><option value="ativo">Ativo</option><option value="concluido">Concluido</option><option value="cancelado">Cancelado</option></select></div><div class="field"><label>Recorrencia</label><select name="recorrencia_meses"><option value="0">Sem recorrencia</option><option value="1">1 vez por mes</option><option value="3">A cada 3 meses</option><option value="12">1 vez por ano</option></select></div><div class="field"><label>Mensagem</label><textarea name="mensagem" required></textarea></div><div class="field"><label>Pessoas que receberao</label><div id="pessoas"></div></div><div class="row"><button class="btn" id="submit-btn">Cadastrar lembrete</button><button class="btn secondary hidden" id="cancel-edit" type="button">Cancelar edicao</button></div></form></section><section class="panel"><h2>Lembretes</h2><div class="table-wrap"><table><thead><tr><th>Titulo</th><th>Data/hora</th><th>Status</th><th>Destinatarios</th><th>Acoes</th></tr></thead><tbody id="rows"></tbody></table></div></section></main><script>Layout.mount('empresa','lembretes');</script><script src="/cliente/lembretes.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>Lembrete</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>Lembrete</h1></div><section class="panel"><div class="section-head"><h2>Lembretes</h2><div class="section-actions"><button class="search-icon-btn" type="button" onclick="alternarBuscaLembretes()" aria-label="Buscar lembretes" 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><button class="btn" type="button" onclick="abrirNovoLembrete()">Novo</button></div></div><div id="lembrete-search" class="search-strip hidden"><input id="lembrete-search-input" placeholder="Buscar por titulo, mensagem, status ou destinatario"></div><div class="table-wrap"><table><thead><tr><th>Titulo</th><th>Data/hora</th><th>Status</th><th>Destinatarios</th><th>Acoes</th></tr></thead><tbody id="rows"></tbody></table></div></section><div id="lembrete-modal" class="modal hidden"><div class="modal-backdrop" onclick="fecharLembreteModal()"></div><section class="modal-panel"><div class="modal-head"><h2 id="lembrete-modal-title">Novo lembrete</h2><button class="btn secondary" type="button" onclick="fecharLembreteModal()">Fechar</button></div><div id="lembrete-modal-body"></div></section></div></main><script>Layout.mount('empresa','lembretes');</script><script src="/cliente/lembretes.js"></script></body></html>
|
||||
|
||||
@@ -1,20 +1,108 @@
|
||||
const form=document.querySelector('#lembrete-form'),rows=document.querySelector('#rows'),pessoasEl=document.querySelector('#pessoas'),submitBtn=document.querySelector('#submit-btn'),cancelBtn=document.querySelector('#cancel-edit');
|
||||
let pessoas=[],lembretes=[];
|
||||
const rows=document.querySelector('#rows'),modal=document.querySelector('#lembrete-modal'),modalTitle=document.querySelector('#lembrete-modal-title'),modalBody=document.querySelector('#lembrete-modal-body');
|
||||
let pessoas=[],lembretes=[],form=null,refreshTimer=null;
|
||||
|
||||
function esc(v){return String(v??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]))}
|
||||
function toInputDate(v){if(!v)return'';const d=new Date(v);if(Number.isNaN(d.getTime()))return'';const pad=n=>String(n).padStart(2,'0');return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`}
|
||||
function recLabel(v){return {1:"Mensal",3:"Trimestral",12:"Anual"}[Number(v)]||"Sem recorrencia"}
|
||||
function selectedDestIds(){return new Set([...form.querySelectorAll('input[name=dest]:checked')].map(x=>String(x.value)))}
|
||||
function termo(){return document.querySelector('#lembrete-search-input')?.value.trim().toLowerCase()||''}
|
||||
function selectedDestIds(){return form?new Set([...form.querySelectorAll('input[name=dest]:checked')].map(x=>String(x.value))):new Set()}
|
||||
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 '<form id="lembrete-form" class="grid cols"><input type="hidden" name="id"><div class="field"><label>Titulo</label><input name="titulo" required></div><div class="field"><label>Data/hora</label><input name="data_hora" type="datetime-local"></div><div class="field"><label>Status</label><select name="status"><option value="ativo">Ativo</option><option value="concluido">Concluido</option><option value="cancelado">Cancelado</option></select></div><div class="field"><label>Recorrencia</label><select name="recorrencia_meses"><option value="0">Sem recorrencia</option><option value="1">1 vez por mes</option><option value="3">A cada 3 meses</option><option value="12">1 vez por ano</option></select></div><div class="field"><label>Mensagem</label><textarea name="mensagem" required></textarea></div><div class="field"><label>Pessoas que receberao</label><div id="pessoas"></div></div><div class="row"><button class="btn" id="submit-btn">Cadastrar lembrete</button><button class="btn secondary" type="button" onclick="fecharLembreteModal()">Cancelar</button></div></form>';
|
||||
}
|
||||
|
||||
function bindForm(){
|
||||
form=document.querySelector('#lembrete-form');
|
||||
renderPessoasForm();
|
||||
form.onsubmit=async e=>{
|
||||
e.preventDefault();
|
||||
const b=Util.formData(form);
|
||||
b.destinatarios=[...form.querySelectorAll('input[name=dest]:checked')].map(x=>Number(x.value));
|
||||
try{
|
||||
if(form.id.value){
|
||||
await Api.put(`/api/lembretes/${form.id.value}`,b);
|
||||
Util.toast('Lembrete atualizado');
|
||||
}else{
|
||||
const r=await Api.post('/api/lembretes',b);
|
||||
const en=r.envio||{};
|
||||
Util.toast(en.enfileirado?'Lembrete enfileirado para envio':(en.falhas?('Lembrete salvo. Enviados: '+(en.enviados||0)+'. Falhas: '+en.falhas):'Lembrete cadastrado'));
|
||||
}
|
||||
fecharLembreteModal();
|
||||
await load();
|
||||
}catch(err){Util.toast(err.message,true)}
|
||||
};
|
||||
}
|
||||
|
||||
function renderPessoasForm(){
|
||||
const pessoasEl=document.querySelector('#pessoas');
|
||||
if(!pessoasEl)return;
|
||||
pessoasEl.innerHTML='<button class="btn secondary" type="button" onclick="abrirNotificacao()">Notificação</button> <span id="notif-summary" class="muted">Nenhuma pessoa selecionada</span><div id="pessoas-hidden" class="hidden">'+pessoas.map(p=>'<label><input type="checkbox" name="dest" value="'+p.id+'"> '+esc(p.nome)+' ('+esc(p.cargo||'')+')</label>').join('')+'</div>';
|
||||
updateNotifSummary();
|
||||
}
|
||||
|
||||
function renderLembretes(){
|
||||
const q=termo();
|
||||
const lista=lembretes.filter(l=>{
|
||||
const dest=(l.destinatarios||[]).map(x=>x.nome).join(' ');
|
||||
return !q||[l.titulo,l.mensagem,l.status,dest,recLabel(l.recorrencia_meses)].some(v=>String(v??'').toLowerCase().includes(q));
|
||||
});
|
||||
rows.innerHTML=lista.map(l=>{
|
||||
const dest=(l.destinatarios||[]).map(x=>x.nome).join(', ')||'-';
|
||||
const acoes=l.pode_alterar?`<button class="btn" onclick="enviar(${l.id})">Enviar</button> ${l.pode_editar?`<button class="btn secondary" onclick="editar(${l.id})">Editar</button>`:''} <button class="btn danger" onclick="excluir(${l.id})">Excluir</button>`:'-';
|
||||
return `<tr><td><strong>${esc(l.titulo)}</strong><br><span class="muted">${esc(l.mensagem)}</span></td><td>${Util.date(l.data_hora)}<br><span class="muted">${recLabel(l.recorrencia_meses)}</span></td><td>${Util.badge(l.status)}</td><td>${esc(dest)}</td><td>${acoes}</td></tr>`;
|
||||
}).join('')||'<tr><td colspan="5">Nenhum lembrete encontrado.</td></tr>';
|
||||
}
|
||||
|
||||
async function loadPessoas(){const d=await Api.get('/api/lembrete-pessoas');pessoas=d.pessoas||[]}
|
||||
async function load(){try{const d=await Api.get('/api/lembretes');lembretes=d.lembretes||[];renderLembretes()}catch(e){Util.toast(e.message,true)}}
|
||||
|
||||
window.abrirNovoLembrete=()=>{
|
||||
modalTitle.textContent='Novo lembrete';
|
||||
modalBody.innerHTML=formHtml();
|
||||
modal.classList.remove('hidden');
|
||||
bindForm();
|
||||
};
|
||||
|
||||
window.fecharLembreteModal=()=>{
|
||||
modal.classList.add('hidden');
|
||||
modalBody.innerHTML='';
|
||||
form=null;
|
||||
};
|
||||
|
||||
window.alternarBuscaLembretes=()=>{
|
||||
const strip=document.querySelector('#lembrete-search'),input=document.querySelector('#lembrete-search-input');
|
||||
strip.classList.toggle('hidden');
|
||||
if(!strip.classList.contains('hidden'))input.focus();
|
||||
};
|
||||
|
||||
function ensureNotifModal(){let m=document.querySelector('#notif-modal');if(m)return m;document.body.insertAdjacentHTML('beforeend','<div id="notif-modal" class="modal hidden"><div class="modal-backdrop" onclick="fecharNotificacao()"></div><section class="modal-panel"><div class="modal-head"><h2>Notificação</h2><button class="btn secondary" type="button" onclick="fecharNotificacao()">Fechar</button></div><div id="notif-modal-body"></div><div class="row"><button class="btn" type="button" onclick="salvarNotificacao()">Salvar</button><button class="btn secondary" type="button" onclick="fecharNotificacao()">Cancelar</button></div></section></div>');return document.querySelector('#notif-modal')}
|
||||
window.abrirNotificacao=()=>{const m=ensureNotifModal(),ids=selectedDestIds();document.querySelector('#notif-modal-body').innerHTML=pessoas.map(p=>'<label><input type="checkbox" value="'+p.id+'" '+(ids.has(String(p.id))?'checked':'')+'> '+esc(p.nome)+' ('+esc(p.cargo||'')+')</label>').join('')||'<p class="muted">Nenhum funcionario ativo.</p>';m.classList.remove('hidden')};
|
||||
window.abrirNotificacao=()=>{if(!form)return;const m=ensureNotifModal(),ids=selectedDestIds();document.querySelector('#notif-modal-body').innerHTML=pessoas.map(p=>'<label><input type="checkbox" value="'+p.id+'" '+(ids.has(String(p.id))?'checked':'')+'> '+esc(p.nome)+' ('+esc(p.cargo||'')+')</label>').join('')||'<p class="muted">Nenhum funcionario ativo.</p>';m.classList.remove('hidden')};
|
||||
window.fecharNotificacao=()=>document.querySelector('#notif-modal')?.classList.add('hidden');
|
||||
window.salvarNotificacao=()=>{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()};
|
||||
async function loadPessoas(){const d=await Api.get('/api/lembrete-pessoas');pessoas=d.pessoas||[];pessoasEl.innerHTML='<button class="btn secondary" type="button" onclick="abrirNotificacao()">Notificação</button> <span id="notif-summary" class="muted">Nenhuma pessoa selecionada</span><div id="pessoas-hidden" class="hidden">'+pessoas.map(p=>'<label><input type="checkbox" name="dest" value="'+p.id+'"> '+esc(p.nome)+' ('+esc(p.cargo||'')+')</label>').join('')+'</div>';updateNotifSummary()}
|
||||
async function load(){try{const d=await Api.get('/api/lembretes');lembretes=d.lembretes||[];rows.innerHTML=lembretes.map(l=>{const dest=(l.destinatarios||[]).map(x=>x.nome).join(', ')||'-';const acoes=l.pode_alterar?`<button class="btn" onclick="enviar(${l.id})">Enviar</button> ${l.pode_editar?`<button class="btn secondary" onclick="editar(${l.id})">Editar</button>`:''} <button class="btn danger" onclick="excluir(${l.id})">Excluir</button>`:'-';return `<tr><td><strong>${esc(l.titulo)}</strong><br><span class="muted">${esc(l.mensagem)}</span></td><td>${Util.date(l.data_hora)}<br><span class="muted">${recLabel(l.recorrencia_meses)}</span></td><td>${Util.badge(l.status)}</td><td>${esc(dest)}</td><td>${acoes}</td></tr>`}).join('')||'<tr><td colspan="5">Nenhum lembrete cadastrado.</td></tr>'}catch(e){Util.toast(e.message,true)}}
|
||||
function resetForm(){form.reset();form.id.value='';form.recorrencia_meses.value='0';submitBtn.textContent='Cadastrar lembrete';cancelBtn.classList.add('hidden');form.querySelectorAll('input[name=dest]').forEach(x=>x.checked=false);updateNotifSummary()}
|
||||
window.editar=id=>{const l=lembretes.find(x=>Number(x.id)===Number(id));if(!l)return;if(!l.pode_editar){Util.toast('Este lembrete ja foi enviado e nao pode mais ser editado.',true);return;}form.id.value=l.id;form.titulo.value=l.titulo||'';form.mensagem.value=l.mensagem||'';form.data_hora.value=toInputDate(l.data_hora);form.status.value='ativo';form.recorrencia_meses.value=String(l.recorrencia_meses||0);const ids=new Set((l.destinatarios||[]).map(x=>Number(x.id)));form.querySelectorAll('input[name=dest]').forEach(x=>x.checked=ids.has(Number(x.value)));updateNotifSummary();submitBtn.textContent='Salvar alteracao';cancelBtn.classList.remove('hidden');scrollTo({top:0,behavior:'smooth'})};
|
||||
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()};
|
||||
|
||||
window.editar=id=>{
|
||||
const l=lembretes.find(x=>Number(x.id)===Number(id));
|
||||
if(!l)return;
|
||||
if(!l.pode_editar){Util.toast('Este lembrete ja foi enviado e nao pode mais ser editado.',true);return;}
|
||||
modalTitle.textContent='Editar lembrete';
|
||||
modalBody.innerHTML=formHtml();
|
||||
modal.classList.remove('hidden');
|
||||
bindForm();
|
||||
form.id.value=l.id;
|
||||
form.titulo.value=l.titulo||'';
|
||||
form.mensagem.value=l.mensagem||'';
|
||||
form.data_hora.value=toInputDate(l.data_hora);
|
||||
form.status.value='ativo';
|
||||
form.recorrencia_meses.value=String(l.recorrencia_meses||0);
|
||||
const ids=new Set((l.destinatarios||[]).map(x=>Number(x.id)));
|
||||
form.querySelectorAll('input[name=dest]').forEach(x=>x.checked=ids.has(Number(x.value)));
|
||||
document.querySelector('#submit-btn').textContent='Salvar alteracao';
|
||||
updateNotifSummary();
|
||||
};
|
||||
|
||||
window.enviar=async id=>{try{const r=await Api.post(`/api/lembretes/${id}/enviar`,{});const en=r.envio||{};Util.toast(en.falhas?("Enviados: "+(en.enviados||0)+". Falhas: "+en.falhas):("Lembrete enviado para "+(en.enviados||0)));await load()}catch(e){Util.toast(e.message,true)}};
|
||||
window.excluir=async id=>{try{await Api.request(`/api/lembretes/${id}`,{method:'DELETE'});Util.toast('Lembrete excluido');await load();resetForm()}catch(e){Util.toast(e.message,true)}};
|
||||
cancelBtn.onclick=resetForm;
|
||||
form.onsubmit=async e=>{e.preventDefault();const b=Util.formData(form);b.destinatarios=[...form.querySelectorAll('input[name=dest]:checked')].map(x=>Number(x.value));try{if(form.id.value){await Api.put(`/api/lembretes/${form.id.value}`,b);Util.toast('Lembrete atualizado')}else{const r=await Api.post('/api/lembretes',b);const en=r.envio||{};Util.toast(en.enfileirado?'Lembrete enfileirado para envio':(en.falhas?('Lembrete salvo. Enviados: '+(en.enviados||0)+'. Falhas: '+en.falhas):'Lembrete cadastrado'))}resetForm();await load()}catch(err){Util.toast(err.message,true)}};
|
||||
(async()=>{try{await loadPessoas();await load();setInterval(load,5000)}catch(e){Util.toast(e.message,true)}})();
|
||||
window.excluir=async id=>{try{await Api.request(`/api/lembretes/${id}`,{method:'DELETE'});Util.toast('Lembrete excluido');await load();fecharLembreteModal()}catch(e){Util.toast(e.message,true)}};
|
||||
|
||||
document.querySelector('#lembrete-search-input')?.addEventListener('input',renderLembretes);
|
||||
(async()=>{try{await loadPessoas();await load();refreshTimer=setInterval(load,5000)}catch(e){Util.toast(e.message,true)}})();
|
||||
|
||||
Reference in New Issue
Block a user