import { useState, useMemo } from 'react'; import { useCollection, useIdentity } from '@deplixo/sdk'; import { Icon } from './components/icons.jsx'; import { Dashboard } from './components/Dashboard.jsx'; import { Payables } from './components/Payables.jsx'; import { Expenses } from './components/Expenses.jsx'; import { Categories, Accounts, Reminders, Settings } from './components/Simple.jsx'; import { AIView } from './components/AIView.jsx'; import { Reports } from './components/Reports.jsx'; import { PayableDialog, ExpenseDialog, CategoryDialog, AccountDialog, ReminderDialog, PayDialog, } from './components/Dialogs.jsx'; const DEFAULT_CATEGORIES = [ 'Alimentação', 'Transporte', 'Moradia', 'Saúde', 'Educação', 'Compras', 'Lazer', 'Serviços', 'Impostos', 'Assinaturas', 'Outros', ].map((n, i) => ({ id: `default-${i}`, name: n, isDefault: true })); const TABS = [ { key: 'home', label: 'Início', Icon: Icon.Home }, { key: 'payables', label: 'Contas', Icon: Icon.Bill }, { key: 'expenses', label: 'Despesas', Icon: Icon.Cart }, { key: 'reports', label: 'Relatórios', Icon: Icon.TrendUp }, { key: 'categories', label: 'Categorias', Icon: Icon.Tag }, { key: 'accounts', label: 'Cartões', Icon: Icon.Card }, { key: 'reminders', label: 'Lembretes', Icon: Icon.Bell }, { key: 'ai', label: 'IA / WhatsApp', Icon: Icon.Chat }, { key: 'settings', label: 'Ajustes', Icon: Icon.Settings }, ]; // Given a due date (YYYY-MM-DD) and a recurrence, compute the next due date. function nextDueDate(dueDate, recurrence, recurrenceDays) { if (!dueDate || !recurrence || recurrence === 'none') return null; const [y, m, d] = dueDate.split('-').map(Number); const dt = new Date(y, (m || 1) - 1, d || 1); if (Number.isNaN(dt.getTime())) return null; switch (recurrence) { case 'weekly': dt.setDate(dt.getDate() + 7); break; case 'biweekly': dt.setDate(dt.getDate() + 14); break; case 'monthly': dt.setMonth(dt.getMonth() + 1); break; case 'yearly': dt.setFullYear(dt.getFullYear() + 1); break; case 'custom': { const step = Math.max(1, Number(recurrenceDays) || 0); if (!step) return null; dt.setDate(dt.getDate() + step); break; } default: return null; } const pad = (n) => String(n).padStart(2, '0'); return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}`; } function App() { const { user } = useIdentity(); const [tab, setTab] = useState('home'); // Filters const [payableFilter, setPayableFilter] = useState('all'); const [expenseFilter, setExpenseFilter] = useState('all'); // Dialog open state const [payableOpen, setPayableOpen] = useState(false); const [expenseOpen, setExpenseOpen] = useState(false); const [categoryOpen, setCategoryOpen] = useState(false); const [accountOpen, setAccountOpen] = useState(false); const [reminderOpen, setReminderOpen] = useState(false); const [payTarget, setPayTarget] = useState(null); const [notifSettings, setNotifSettings] = useState({ dueTomorrow: true, dueToday: true, overdue: true, dailySummary: false, weeklySummary: false, monthlySummary: true, }); // Data const payC = useCollection('avisae_payables'); const expC = useCollection('avisae_expenses'); const catC = useCollection('avisae_categories'); const accC = useCollection('avisae_accounts'); const remC = useCollection('avisae_reminders'); const filterMine = (items) => (items || []).filter(i => i.author?.id === user?.id); const payables = useMemo( () => filterMine(payC.items).map(i => ({ id: i.id, ...i.value })), [payC.items, user?.id] ); const expenses = useMemo( () => filterMine(expC.items).map(i => ({ id: i.id, ...i.value })), [expC.items, user?.id] ); const userCats = useMemo( () => filterMine(catC.items).map(i => ({ id: i.id, ...i.value })), [catC.items, user?.id] ); const accounts = useMemo( () => filterMine(accC.items).map(i => ({ id: i.id, ...i.value })), [accC.items, user?.id] ); const reminders = useMemo( () => filterMine(remC.items).map(i => ({ id: i.id, ...i.value })), [remC.items, user?.id] ); const categories = useMemo(() => { // Merge default + user categories (avoid duplicates by name) const names = new Set(userCats.map(c => c.name.toLowerCase())); const defs = DEFAULT_CATEGORIES.filter(c => !names.has(c.name.toLowerCase())); return [...defs, ...userCats]; }, [userCats]); // Handlers const savePayable = (v) => payC.add(v); const saveExpense = (v) => expC.add(v); const saveCategory = (v) => catC.add(v); const saveAccount = (v) => accC.add(v); const saveReminder = (v) => remC.add(v); const deletePayable = (id) => payC.remove(id); const deleteExpense = (id) => expC.remove(id); const deleteCategory = (id) => catC.remove(id); const deleteAccount = (id) => accC.remove(id); const deleteReminder = (id) => remC.remove(id); const toggleReminder = (r) => { remC.update(r.id, { ...r, active: !(r.active !== false) }); }; const confirmPayment = (paymentData) => { if (!payTarget) return; payC.update(payTarget.id, { ...payTarget, status: 'paid', ...paymentData, }); // Auto-generate the next occurrence for recurring bills const rec = payTarget.recurrence; if (rec && rec !== 'none') { const nextDate = nextDueDate(payTarget.dueDate, rec, payTarget.recurrenceDays); if (nextDate) { const { id, status, paidAt, paidAmount, paymentNotes, ...base } = payTarget; payC.add({ ...base, dueDate: nextDate, status: 'pending', generatedFrom: id, }); } } setPayTarget(null); }; const initials = (user?.name || 'U').slice(0, 2).toUpperCase(); return (