import React, { useState, useMemo } from 'react'; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { TrendingUp, TrendingDown, AlertCircle, ArrowUpRight, Calendar as CalendarIcon, Trophy, Award, ShoppingBag, ChevronLeft, ChevronRight, CheckCircle2, PieChart, Wallet } from 'lucide-react'; import { Company, Expense, Receivable, FinancialSummary } from '../types'; import { AIInsightsWidget } from './AIInsightsWidget'; interface DashboardViewProps { financialSummary: FinancialSummary; companies: Company[]; expenses: Expense[]; receivables: Receivable[]; } const StatCard = ({ icon: Icon, label, value, subtext, type = 'neutral' }: any) => { // Cores adaptadas para o estilo "Larkon": fundos suaves e cores de destaque const iconStyle = type === 'success' ? 'bg-orange-500 text-white shadow-lg shadow-orange-500/30' : type === 'danger' ? 'bg-red-500 text-white shadow-lg shadow-red-500/30' : type === 'warning' ? 'bg-blue-500 text-white shadow-lg shadow-blue-500/30' : 'bg-slate-800 text-white shadow-lg shadow-slate-800/30'; const trendColor = type === 'success' ? 'text-green-500' : type === 'danger' ? 'text-red-500' : 'text-slate-400'; return (
{/* Mock Sparkline or Trend Indicator */}
{type === 'success' ? : } {type === 'success' ? '+12%' : type === 'danger' ? '-5%' : '0%'}

{value}

{label}

{subtext &&

{subtext}

}
); }; export const DashboardView: React.FC = ({ financialSummary, companies, expenses, receivables }) => { const [currentDate, setCurrentDate] = useState(new Date()); // Inicia com a data atual real // --- LÓGICA DO GRÁFICO DINÂMICO --- const chartData = useMemo(() => { const data = []; const today = new Date(); // Gerar os últimos 3 meses e próximos 2 meses for (let i = -3; i <= 2; i++) { const d = new Date(today.getFullYear(), today.getMonth() + i, 1); const monthKey = d.toLocaleString('pt-BR', { month: 'short' }); const yearIso = String(d.getFullYear()); const monthIso = String(d.getMonth() + 1).padStart(2, '0'); const ym = `${yearIso}-${monthIso}`; // Somar Receitas do Mês (Estritamente baseada em Receivables) const monthlyRevenue = receivables .filter(r => r.dueDate.startsWith(ym)) .reduce((sum, r) => sum + r.value, 0); // Somar Despesas do Mês const monthlyExpense = expenses .filter(e => e.dueDate.startsWith(ym)) .reduce((sum, e) => sum + e.amount, 0); data.push({ name: monthKey.charAt(0).toUpperCase() + monthKey.slice(1), fullDate: ym, receita: monthlyRevenue, despesa: monthlyExpense, isCurrent: i === 0 }); } return data; }, [expenses, receivables]); // --- LOGICA DE RANKING (Estritamente Financeira) --- // 1. Top Clientes (Baseado APENAS em Contas a Receber existentes) const topClients = useMemo(() => { // Agrupar recebíveis por empresa const clientMap: Record = {}; receivables.forEach(r => { if (!clientMap[r.companyName]) { clientMap[r.companyName] = { name: r.companyName, total: 0, count: 0 }; } clientMap[r.companyName].total += r.value; clientMap[r.companyName].count += 1; }); return Object.values(clientMap) .sort((a, b) => b.total - a.total) .slice(0, 5); }, [receivables]); // Depende apenas de receivables // 2. Serviços Mais Vendidos (Baseado APENAS em Contas a Receber existentes) const topServices = useMemo(() => { const serviceCounts: Record = {}; receivables.forEach(r => { // Usamos a descrição como chave, pois o ID do serviço original não é salvo no receivable (simplificação) const key = r.description; if (!serviceCounts[key]) { serviceCounts[key] = { name: r.description, count: 0, revenue: 0, category: r.category }; } serviceCounts[key].count += 1; serviceCounts[key].revenue += r.value; }); return Object.values(serviceCounts).sort((a, b) => b.count - a.count).slice(0, 5); }, [receivables]); // Depende apenas de receivables // 3. Menor Inadimplência / Fidelidade (Mantido lógica de empresa pois é atributo de cadastro) const bestPayers = useMemo(() => { return [...companies] .filter(c => c.status === 'active') .sort((a, b) => parseInt(a.since) - parseInt(b.since)) .slice(0, 5); }, [companies]); // --- LOGICA DO CALENDÁRIO --- const handlePrevMonth = () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); const handleNextMonth = () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); const getDaysInMonth = (year: number, month: number) => new Date(year, month + 1, 0).getDate(); const getFirstDayOfMonth = (year: number, month: number) => new Date(year, month, 1).getDay(); const daysInMonth = getDaysInMonth(currentDate.getFullYear(), currentDate.getMonth()); const firstDay = getFirstDayOfMonth(currentDate.getFullYear(), currentDate.getMonth()); const days = []; for (let i = 0; i < firstDay; i++) { days.push(
); } const todayStr = new Date().toISOString().split('T')[0]; for (let d = 1; d <= daysInMonth; d++) { const dateStr = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; const isToday = dateStr === todayStr; // Mapeando eventos (Despesas Reais e Recebimentos Reais) const dayExpenses = expenses.filter(e => e.dueDate === dateStr); const dayIncomes = receivables.filter(r => r.dueDate === dateStr); days.push(
{d}
{dayExpenses.map(exp => (
-{exp.amount.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', maximumFractionDigits: 0 })}
))} {dayIncomes.map(inc => (
+{inc.value.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', maximumFractionDigits: 0 })}
))}
); } // Verifica se há dados para exibir no gráfico const hasChartData = chartData.some(d => d.receita > 0 || d.despesa > 0); return (
{/* AI Insights Widget */} {/* KPI Cards Row */}
= 0 ? 'success' : 'danger'} />
{/* Left Column (Main Chart) */}
{/* Financial Trend Chart */}

Fluxo de Caixa

Desempenho financeiro em tempo real

Receitas Despesas
{hasChartData ? ( [`R$ ${value.toLocaleString('pt-BR')}`, '']} /> {/* Orange for Revenue (Primary), Grey for Expenses (Secondary) */} ) : (

Sem movimentações no período

)}
{/* CALENDÁRIO FINANCEIRO */}

Agenda Financeira

{currentDate.toLocaleDateString('pt-BR', { month: 'long', year: 'numeric' })}
{['D', 'S', 'T', 'Q', 'Q', 'S', 'S'].map(d => (
{d}
))}
{days}
{/* Right Column: RANKINGS & Profit */}
{/* Top Products/Services Widget */}

Top Serviços

{topServices.length > 0 ? topServices.map((srv, idx) => (
{srv.name}
{srv.count} vendas
R$ {srv.revenue.toLocaleString('pt-BR')}
)) : (

Nenhum serviço faturado

)}
{/* Ranking: Top Clientes */}

Melhores Clientes

{topClients.length > 0 ? topClients.map((client, idx) => (
{idx + 1}
{client.name}
R$ {client.total.toLocaleString('pt-BR')}
)) : (

Sem dados

)}
{/* Ranking: Menor Inadimplência (Fidelidade) */}

Clientes Confiáveis

Baseado no histórico de pagamentos.

{bestPayers.map((client) => ( {client.fantasyName || client.name} ))}
); };