// SGAB v2 — Agenda Mensal Visual const { useState, useMemo } = React; const DIAS_SEMANA = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb']; const MESES = ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro']; function Agenda({ user, toast }) { const isAdmin = user.perfil === 'administrador'; const hoje = new Date(); const [ano, setAno] = useState(hoje.getFullYear()); const [mes, setMes] = useState(hoje.getMonth()); const [db, setDb] = useState(() => SGAB.getDB()); const [modal, setModal] = useState(false); const [selDia, setSelDia] = useState(null); const [editando, setEditando] = useState(null); const [form, setForm] = useState({}); const { fmt } = SGAB; function reload() { setDb(SGAB.getDB()); } // Dias do mês const diasDoMes = useMemo(() => { const primeiroDia = new Date(ano, mes, 1).getDay(); const totalDias = new Date(ano, mes + 1, 0).getDate(); const cells = []; for (let i = 0; i < primeiroDia; i++) cells.push(null); for (let d = 1; d <= totalDias; d++) cells.push(d); // pad to complete last row while (cells.length % 7 !== 0) cells.push(null); return cells; }, [ano, mes]); function dataStr(d) { return `${ano}-${String(mes+1).padStart(2,'0')}-${String(d).padStart(2,'0')}`; } function entradasDia(d) { if (!d) return []; return db.agenda.filter(e => e.data === dataStr(d)); } // Totais do mês const totais = useMemo(() => { const prefix = `${ano}-${String(mes+1).padStart(2,'0')}`; const entries = db.agenda.filter(e => e.data.startsWith(prefix)); return { creditos: entries.filter(e=>e.tipo==='credito').reduce((s,e)=>s+(e.valor||0),0), debitos: entries.filter(e=>e.tipo==='debito').reduce((s,e)=>s+(e.valor||0),0), }; }, [db, ano, mes]); function navMes(dir) { let nm = mes + dir, na = ano; if (nm < 0) { nm = 11; na--; } if (nm > 11) { nm = 0; na++; } setMes(nm); setAno(na); } function openModal(dia, entrada=null) { setSelDia(dia); if (entrada) { setEditando(entrada); setForm({ data:entrada.data, tipo:entrada.tipo, desc:entrada.desc, valor:entrada.valor||'', clienteId:entrada.clienteId||'', status:entrada.status, riscado:entrada.riscado }); } else { setEditando(null); const dataInicial = dia ? dataStr(dia) : fmt.today(); setForm({ data:dataInicial, tipo:'servico', desc:'', valor:'', clienteId:'', status:'agendado', riscado:false }); } setModal(true); } function salvar() { if (!form.desc || !form.desc.trim()) { toast('Informe a descrição.','error'); return; } if (!form.data) { toast('Informe a data.','error'); return; } const newDb = SGAB.getDB(); const payload = { ...form, valor: form.valor?+form.valor:null, clienteId:form.clienteId?+form.clienteId:null }; if (editando) { const idx = newDb.agenda.findIndex(e=>e.id===editando.id); if (idx>=0) newDb.agenda[idx] = { ...newDb.agenda[idx], ...payload }; } else { const id = SGAB.nxtId(newDb,'agenda'); newDb.agenda.push({ id, ...payload }); } SGAB.saveDB(newDb); reload(); setModal(false); // Reposicionar calendário no mês da data salva const [yy, mm] = form.data.split('-'); setAno(+yy); setMes(+mm - 1); toast(editando?'Entrada atualizada!':'Entrada adicionada!'); } function excluir(entrada) { if (!confirm('Excluir esta entrada?')) return; const newDb = SGAB.getDB(); newDb.agenda = newDb.agenda.filter(e=>e.id!==entrada.id); SGAB.saveDB(newDb); reload(); toast('Entrada removida.','info'); } function toggleRiscado(entrada) { const newDb = SGAB.getDB(); const idx = newDb.agenda.findIndex(e=>e.id===entrada.id); if (idx>=0) newDb.agenda[idx].riscado = !newDb.agenda[idx].riscado; SGAB.saveDB(newDb); reload(); } function toggleFinalizado(entrada) { const newDb = SGAB.getDB(); const idx = newDb.agenda.findIndex(e=>e.id===entrada.id); if (idx>=0) newDb.agenda[idx].status = newDb.agenda[idx].status==='finalizado'?'agendado':'finalizado'; SGAB.saveDB(newDb); reload(); } // Cell entry style function entryStyle(e) { const base = { fontSize:11, padding:'2px 6px', borderRadius:4, marginBottom:2, cursor:'pointer', textDecoration:e.riscado?'line-through':'none', display:'flex', alignItems:'center', gap:4, lineHeight:1.3, wordBreak:'break-word', transition:'opacity 0.15s', opacity:e.riscado?0.55:1 }; if (e.tipo==='credito') return {...base, color:'#15803D', background:'#F0FDF4', border:'1px solid #BBF7D0', fontWeight:600}; if (e.tipo==='debito') return {...base, color:'#B91C1C', background:'#FEF2F2', border:'1px solid #FECACA', fontWeight:600}; if (e.tipo==='feriado') return {...base, color:'#6B7280', background:'#F1F5F9', border:'1px solid #E2E8F0', fontWeight:600, fontStyle:'italic'}; // servico const bg = e.status==='finalizado' ? '#FEF9C3' : '#fff'; const bc = e.status==='finalizado' ? '#FDE68A' : '#E2E8F0'; return {...base, color:'#374151', background:bg, border:`1px solid ${bc}`}; } const tiposEntrada = [ {v:'servico', l:'Serviço / Atendimento'}, {v:'credito', l:'Crédito (recebimento)'}, {v:'debito', l:'Débito (despesa)'}, {v:'feriado', l:'Feriado / Bloqueio'}, ]; const cliOpts = db.clientes.map(c=>({v:c.id,l:c.fantasia})); return (