/* ============================================================ AGENDA — Dashboard (indicadores calculados de dados reais) ============================================================ */ const { Icon: I1, Avatar: Av, Badge: Bdg, Button: Btn, Checkbox: Cbx } = window; function Dashboard(ctx) { const { terms, appts, services, team, clients, tasks, transactions, openNewAppt, openAppt, statusMap, money, settings, user, businessType } = ctx; const DB = window.DB; const todayISO = DB.iso(DB.today); const todays = appts.filter(a => a.date === todayISO).sort((a, b) => window.timeToMin(a.start) - window.timeToMin(b.start)); const active = todays.filter(a => a.status !== 'cancelado'); const txToday = transactions.filter(t => t.date === todayISO && t.value > 0); const revToday = txToday.reduce((s, t) => s + t.value, 0); const pendingSum = transactions.filter(t => t.status === 'pendente' && t.value > 0).reduce((s, t) => s + t.value, 0); const confirmed = active.filter(a => a.status === 'confirmado').length; const confRate = active.length ? Math.round(confirmed / active.length * 100) : 0; const nowMin = new Date().getHours() * 60 + new Date().getMinutes(); const nextAppt = active.find(a => window.timeToMin(a.start) >= nowMin && (a.status === 'confirmado' || a.status === 'pendente')); const hour = new Date().getHours(); const greet = hour < 12 ? 'Bom dia' : hour < 18 ? 'Boa tarde' : 'Boa noite'; const dateStr = DB.today.toLocaleDateString('pt-BR', { weekday: 'long', day: 'numeric', month: 'long' }); const userName = ((user && (user.name || user.login)) || settings.user_name || 'Secretaria').split(' ')[0]; const cli = (id) => clients.find(c => c.id === id); const svc = (id) => services.find(s => s.id === id); const pro = (id) => team.find(p => p.id === id); // comparação com ontem const yCount = appts.filter(a => a.date === DB.iso(DB.addDays(-1)) && a.status !== 'cancelado').length; const dy = active.length - yCount; // clientes novos nos últimos 7 dias const newClients = clients.filter(c => c.createdAt && (Date.now() - new Date(String(c.createdAt).replace(' ', 'T')).getTime()) < 7 * 86400e3).length; const kpis = [ { lbl: terms.appt + 's hoje', val: active.length, ic: 'calendar', color: 'var(--ev-blue)', bg: 'var(--ev-blue-bg)', delta: (dy >= 0 ? '+' : '') + dy, up: dy >= 0, since: 'vs. ontem' }, { lbl: 'Faturamento hoje', val: money(revToday).replace(',00', ''), ic: 'wallet', color: 'var(--ev-green)', bg: 'var(--ev-green-bg)', delta: txToday.length + '', up: true, since: 'lançamentos' }, { lbl: 'Taxa de confirmação', val: confRate + '%', ic: 'check2', color: 'var(--ev-teal)', bg: 'var(--ev-teal-bg)', delta: confirmed + '/' + active.length, up: true, since: 'confirmados' }, { lbl: terms.clients.toLowerCase() + ' novos', val: newClients, ic: 'users', color: 'var(--ev-cyan)', bg: 'var(--ev-cyan-bg)', delta: '+' + newClients, up: true, since: 'últimos 7 dias' }, ]; // ocupação da semana (seg–dom) a partir dos agendamentos reais const weekStart = (() => { const d = new Date(DB.today); const wd = (d.getDay() + 6) % 7; d.setDate(d.getDate() - wd); return d; })(); const weekCounts = Array.from({ length: 7 }, (_, i) => { const d = new Date(weekStart); d.setDate(weekStart.getDate() + i); const k = DB.iso(d); return appts.filter(a => a.date === k && a.status !== 'cancelado').length; }); const weekTotal = weekCounts.reduce((s, v) => s + v, 0); const maxWeek = Math.max(1, ...weekCounts); const occ = weekCounts.map(v => Math.round(v / maxWeek * 100)); const todayIdx = (DB.today.getDay() + 6) % 7; const openTasks = tasks.filter(t => !t.done); const pendCount = active.filter(a => a.status === 'pendente').length; // ocupação da área/pessoas do segmento hoje const proLoad = team.filter(p => !p.archived).map(p => { const c = active.filter(a => a.proId === p.id).length; return { ...p, count: c, pct: Math.min(100, c * 22) }; }); return (