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 */}
{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}
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}
))}
);
};