import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { Company, Expense, Receivable, Service, AppUser, Client, FinancialSummary, TenantProfile, Category, Proposal } from '../types'; import { useStickyState } from '../hooks/useStickyState'; // --- MOCK DATA GENERATORS (Moved from App.tsx) --- const getDynamicDate = (daysOffset: number) => { const date = new Date(); date.setDate(date.getDate() + daysOffset); return date.toISOString().split('T')[0]; }; const initialServices: Service[] = [ { id: '1', name: 'Consultoria Financeira', category: 'Consultoria', price: 2500, active: true, description: 'Análise completa.', billingType: 'one-time' }, { id: '2', name: 'Gestão de Mídias', category: 'Marketing', price: 1500, active: true, description: 'Postagens mensais.', billingType: 'recurring' }, { id: '3', name: 'Suporte TI', category: 'TI', price: 800, active: true, description: 'SLA 24h.', billingType: 'recurring' }, { id: '4', name: 'Desenvolvimento Web', category: 'Tecnologia', price: 5000, active: true, description: 'Site institucional.', billingType: 'one-time' }, ]; const initialCompanies: Company[] = [ { id: '1', name: 'Uda Studios Tecnologia LTDA', fantasyName: 'Uda Studios', cnpj: '12.345.678/0001-90', ie: '123.456.789.000', city: 'São Paulo - SP', logo: 'https://i.pravatar.cc/150?u=uda', status: 'active', industry: 'Tecnologia', email: 'financeiro@uda.com', phone: '(11) 3344-5566', address: 'Av. Paulista, 1000, Bela Vista', since: '2022', description: 'Estúdio de software.', contacts: [{ id: 'c1', name: 'Roberto Silva', role: 'CEO', email: 'roberto@uda.com', phone: '11999998888', avatar: 'https://i.pravatar.cc/150?u=1' }], documents: [], activeServices: [initialServices[1], initialServices[3]] }, { id: '2', name: 'Angels Healthcare S.A.', fantasyName: 'Angels Health', cnpj: '98.765.432/0001-10', city: 'Rio de Janeiro - RJ', logo: 'https://i.pravatar.cc/150?u=angels', status: 'active', industry: 'Saúde', email: 'contato@angels.com', phone: '(21) 2233-4455', address: 'Rua da Saúde, 500', since: '2021', description: 'Rede de clínicas.', contacts: [], documents: [], activeServices: [initialServices[0], initialServices[2]] }, { id: '3', name: 'Padaria do João MEI', fantasyName: 'Padaria do João', cnpj: '11.111.111/0001-11', city: 'Belo Horizonte - MG', logo: 'https://i.pravatar.cc/150?u=joao', status: 'overdue', industry: 'Varejo', email: 'joao@padaria.com', phone: '(31) 3333-3333', address: 'Rua do Pão, 10', since: '2023', description: 'Padaria artesanal.', contacts: [], documents: [], activeServices: [initialServices[1]] } ]; const initialExpenses: Expense[] = [ { id: 'e1', title: 'Aluguel Escritório', category: 'Operacional', amount: 3500, dueDate: getDynamicDate(-30), status: 'paid', type: 'fixed' }, { id: 'e2', title: 'Servidor AWS', category: 'TI', amount: 850, dueDate: getDynamicDate(-28), status: 'paid', type: 'variable' }, { id: 'e3', title: 'Energia Elétrica', category: 'Operacional', amount: 450, dueDate: getDynamicDate(-2), status: 'paid', type: 'variable' }, { id: 'e4', title: 'Licença Software CRM', category: 'TI', amount: 200, dueDate: getDynamicDate(5), status: 'pending', type: 'fixed' }, { id: 'e5', title: 'Folha de Pagamento', category: 'Pessoal', amount: 15000, dueDate: getDynamicDate(10), status: 'pending', type: 'fixed' }, { id: 'e6', title: 'DAS Simples Nacional', category: 'Impostos', amount: 1200, dueDate: getDynamicDate(15), status: 'pending', type: 'variable' }, { id: 'e7', title: 'Aluguel Escritório', category: 'Operacional', amount: 3500, dueDate: getDynamicDate(30), status: 'pending', type: 'fixed' }, ]; const initialProposals: Proposal[] = [ { id: 'p1', number: 'PROP-001', clientId: '1', clientName: 'Uda Studios', clientEmail: 'roberto@uda.com', issueDate: getDynamicDate(-2), validUntil: getDynamicDate(5), status: 'sent', totalValue: 7500, items: [ { id: 'i1', description: 'Consultoria Financeira', quantity: 1, unitPrice: 2500, total: 2500 }, { id: 'i2', description: 'Desenvolvimento Web', quantity: 1, unitPrice: 5000, total: 5000 } ], notes: 'Pagamento 50% na entrada e 50% na entrega.' } ]; const initialSuperAdmin: AppUser = { id: 'u1', name: 'Nella Vita', email: 'admin@comfi.com', role: 'super_admin', active: true, avatar: 'https://i.pravatar.cc/150?u=admin', permissions: [] }; const initialUsers: AppUser[] = [ initialSuperAdmin, { id: 'u2', name: 'João Comercial', email: 'vendas@comfi.com', role: 'user', active: true, avatar: 'https://i.pravatar.cc/150?u=joao', permissions: ['crm', 'kanban', 'calendar', 'proposals'] }, { id: 'u3', name: 'Maria Financeiro', email: 'fin@comfi.com', role: 'user', active: true, avatar: 'https://i.pravatar.cc/150?u=maria', permissions: ['dashboard', 'receivables', 'payables', 'invoicing'] } ]; const initialTenant: TenantProfile = { name: 'Minha Empresa S.A.', cnpj: '00.000.000/0001-00', email: 'contato@minhaempresa.com', phone: '(11) 99999-9999', address: 'Av. Brigadeiro Faria Lima, 1234, SP', logo: '', primaryColor: '#f97316' }; const initialCategories: Category[] = [ { id: 'c1', name: 'Operacional', type: 'expense', color: 'bg-red-50 text-red-600' }, { id: 'c2', name: 'Administrativo', type: 'expense', color: 'bg-blue-50 text-blue-600' }, { id: 'c3', name: 'Marketing', type: 'expense', color: 'bg-purple-50 text-purple-600' }, { id: 'c4', name: 'Pessoal', type: 'expense', color: 'bg-yellow-50 text-yellow-600' }, { id: 'c5', name: 'Impostos', type: 'expense', color: 'bg-slate-50 text-slate-600' }, { id: 'c6', name: 'Serviços', type: 'income', color: 'bg-green-50 text-green-600' }, ]; const generateInitialReceivables = (companies: Company[]): Receivable[] => { const receivables: Receivable[] = []; companies.forEach((company, cIndex) => { company.activeServices.forEach((service, sIndex) => { receivables.push({ id: `r-${company.id}-${sIndex}-past`, description: service.name, companyName: company.fantasyName || company.name, category: service.category, value: service.price, dueDate: getDynamicDate(-30 - (cIndex * 2)), status: 'paid', type: service.billingType === 'recurring' ? 'recurring' : 'one-time' }); const isOverdue = company.status === 'overdue' && sIndex === 0; receivables.push({ id: `r-${company.id}-${sIndex}-curr`, description: service.name, companyName: company.fantasyName || company.name, category: service.category, value: service.price, dueDate: getDynamicDate(isOverdue ? -5 : 10 + (cIndex * 3)), status: isOverdue ? 'overdue' : 'pending', type: service.billingType === 'recurring' ? 'recurring' : 'one-time' }); if (service.billingType === 'recurring') { receivables.push({ id: `r-${company.id}-${sIndex}-next`, description: service.name, companyName: company.fantasyName || company.name, category: service.category, value: service.price, dueDate: getDynamicDate(40 + (cIndex * 2)), status: 'pending', type: 'recurring' }); } }); }); return receivables; }; // --- CONTEXT INTERFACE --- interface ComFiContextData { services: Service[]; setServices: (data: Service[]) => void; companies: Company[]; setCompanies: (data: Company[]) => void; expenses: Expense[]; setExpenses: (data: Expense[]) => void; clients: Client[]; setClients: (data: Client[]) => void; receivables: Receivable[]; setReceivables: React.Dispatch>; proposals: Proposal[]; setProposals: (data: Proposal[]) => void; currentUser: AppUser; setCurrentUser: (user: AppUser) => void; users: AppUser[]; setUsers: (users: AppUser[]) => void; financialSummary: FinancialSummary; addReceivable: (receivable: Receivable) => void; tenant: TenantProfile; setTenant: (tenant: TenantProfile) => void; categories: Category[]; setCategories: (categories: Category[]) => void; } const ComFiContext = createContext({} as ComFiContextData); export const ComFiProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { // State Initialization const [services, setServices] = useStickyState(initialServices, 'comfi_services'); const [companies, setCompanies] = useStickyState(initialCompanies, 'comfi_companies'); const [expenses, setExpenses] = useStickyState(initialExpenses, 'comfi_expenses'); const [clients, setClients] = useStickyState([], 'comfi_clients'); const [currentUser, setCurrentUser] = useStickyState(initialSuperAdmin, 'comfi_current_user'); const [users, setUsers] = useStickyState(initialUsers, 'comfi_users'); const [tenant, setTenant] = useStickyState(initialTenant, 'comfi_tenant'); const [categories, setCategories] = useStickyState(initialCategories, 'comfi_categories'); const [proposals, setProposals] = useStickyState(initialProposals, 'comfi_proposals'); const [receivables, setReceivables] = useState(() => { try { const saved = window.localStorage.getItem('comfi_receivables'); if (saved) return JSON.parse(saved); return generateInitialReceivables(initialCompanies); } catch (e) { return generateInitialReceivables(initialCompanies); } }); useEffect(() => { window.localStorage.setItem('comfi_receivables', JSON.stringify(receivables)); }, [receivables]); // Derived State: Financial Summary const financialSummary = useMemo(() => { const currentMonth = new Date().getMonth(); const currentYear = new Date().getFullYear(); const isCurrentMonth = (dateStr: string) => dateStr.startsWith(`${currentYear}-${String(currentMonth + 1).padStart(2, '0')}`); const currentReceivables = receivables.filter(r => isCurrentMonth(r.dueDate)); const currentExpenses = expenses.filter(e => isCurrentMonth(e.dueDate)); const totalRevenue = currentReceivables.reduce((acc, r) => acc + r.value, 0); const totalExpenses = currentExpenses.reduce((acc, expense) => acc + expense.amount, 0); const profit = totalRevenue - totalExpenses; const payablePending = expenses.filter(e => e.status !== 'paid').reduce((acc, expense) => acc + expense.amount, 0); const receivablePending = receivables.filter(r => r.status !== 'paid').reduce((acc, r) => acc + r.value, 0); return { totalRevenue, totalExpenses, profit, payablePending, receivablePending }; }, [receivables, expenses]); const addReceivable = (newRec: Receivable) => { setReceivables(prev => [...prev, newRec]); }; return ( {children} ); }; export const useComFi = () => { const context = useContext(ComFiContext); if (!context) throw new Error('useComFi must be used within a ComFiProvider'); return context; };