Files
ComFi/components/AIInsightsWidget.tsx
MMrp89 1a57ac7754 feat: Initialize ComFi project with Vite
Setup project structure, dependencies, and basic configuration for the ComFi application. Includes initial setup for Vite, React, TypeScript, Tailwind CSS, and essential development tools. Defines core types and provides a basic README for local development.
2026-02-09 20:28:37 -03:00

159 lines
6.4 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Sparkles, TrendingUp, AlertTriangle, Lightbulb, RefreshCw } from 'lucide-react';
import { FinancialSummary, Company, Expense } from '../types';
interface AIInsightsWidgetProps {
financialSummary: FinancialSummary;
topClients: Company[];
expenses: Expense[];
}
export const AIInsightsWidget: React.FC<AIInsightsWidgetProps> = ({ financialSummary, topClients, expenses }) => {
const [isLoading, setIsLoading] = useState(false);
const [insights, setInsights] = useState<{type: 'success' | 'warning' | 'info' | 'danger', title: string, message: string}[]>([]);
const generateInsights = () => {
setIsLoading(true);
// Simulating AI processing time
setTimeout(() => {
const newInsights: typeof insights = [];
// 1. Profitability Analysis
const margin = financialSummary.totalRevenue > 0 ? (financialSummary.profit / financialSummary.totalRevenue) * 100 : 0;
if (margin > 20) {
newInsights.push({
type: 'success',
title: 'Alta Rentabilidade',
message: `Sua margem de lucro de ${margin.toFixed(1)}% está acima da média do mercado.`
});
} else if (margin < 5 && margin > 0) {
newInsights.push({
type: 'warning',
title: 'Margem Apertada',
message: 'Lucro abaixo de 5%. Recomendamos revisão imediata de custos fixos.'
});
} else if (margin <= 0) {
newInsights.push({
type: 'danger',
title: 'Prejuízo Operacional',
message: 'As despesas superaram as receitas. Verifique inadimplência e corte gastos.'
});
} else {
newInsights.push({
type: 'info',
title: 'Estabilidade',
message: 'Sua margem está estável. Busque novas fontes de receita para crescer.'
});
}
// 2. Cash Flow Analysis
if (financialSummary.receivablePending > financialSummary.payablePending) {
newInsights.push({
type: 'info',
title: 'Caixa Saudável',
message: `Previsão de entrada líquida de R$ ${(financialSummary.receivablePending - financialSummary.payablePending).toLocaleString('pt-BR')}.`
});
} else {
newInsights.push({
type: 'danger',
title: 'Risco de Liquidez',
message: 'Contas a pagar superam os recebíveis previstos. Aumente o esforço de cobrança.'
});
}
// 3. Strategic / Growth
if (topClients.length > 0) {
newInsights.push({
type: 'success',
title: 'Oportunidade de Upsell',
message: `O cliente ${topClients[0].fantasyName || topClients[0].name} tem alto potencial. Ofereça novos serviços.`
});
} else {
newInsights.push({
type: 'warning',
title: 'Base de Clientes',
message: 'Cadastre mais clientes para receber insights de vendas personalizados.'
})
}
setInsights(newInsights);
setIsLoading(false);
}, 1500);
};
useEffect(() => {
generateInsights();
}, [financialSummary]);
return (
<div className="bg-white rounded-[1.5rem] p-6 border border-slate-100 shadow-sm relative overflow-hidden transition-all hover:shadow-md">
<div className="flex justify-between items-center mb-6">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center shadow-lg shadow-orange-200">
<Sparkles size={20} className="text-white" />
</div>
<div>
<h3 className="font-bold text-lg text-slate-800 tracking-tight">AI Insights</h3>
<p className="text-slate-400 text-xs font-medium">Análise financeira inteligente</p>
</div>
</div>
<button
onClick={generateInsights}
className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-slate-50 text-slate-400 hover:text-orange-500 transition-all"
title="Atualizar Análise"
>
<RefreshCw size={16} className={isLoading ? "animate-spin" : ""} />
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{isLoading ? (
// Skeleton Loading Style
[1, 2, 3].map(i => (
<div key={i} className="bg-slate-50/50 rounded-2xl p-5 h-28 animate-pulse border border-slate-100">
<div className="h-4 w-24 bg-slate-200 rounded mb-3"></div>
<div className="h-3 w-full bg-slate-200 rounded mb-2"></div>
<div className="h-3 w-2/3 bg-slate-200 rounded"></div>
</div>
))
) : (
insights.map((insight, idx) => (
<div
key={idx}
className={`rounded-2xl p-5 border transition-all duration-300 hover:-translate-y-1 ${
insight.type === 'success' ? 'bg-emerald-50/50 border-emerald-100' :
insight.type === 'warning' ? 'bg-amber-50/50 border-amber-100' :
insight.type === 'danger' ? 'bg-red-50/50 border-red-100' :
'bg-blue-50/50 border-blue-100'
}`}
>
<div className="flex items-center gap-2 mb-2">
{insight.type === 'success' && <TrendingUp size={16} className="text-emerald-500" />}
{insight.type === 'warning' && <AlertTriangle size={16} className="text-amber-500" />}
{insight.type === 'danger' && <AlertTriangle size={16} className="text-red-500" />}
{insight.type === 'info' && <Lightbulb size={16} className="text-blue-500" />}
<h4 className={`font-bold text-sm ${
insight.type === 'success' ? 'text-emerald-800' :
insight.type === 'warning' ? 'text-amber-800' :
insight.type === 'danger' ? 'text-red-800' :
'text-blue-800'
}`}>{insight.title}</h4>
</div>
<p className={`text-xs leading-relaxed font-medium ${
insight.type === 'success' ? 'text-emerald-700/80' :
insight.type === 'warning' ? 'text-amber-700/80' :
insight.type === 'danger' ? 'text-red-700/80' :
'text-blue-700/80'
}`}>
{insight.message}
</p>
</div>
))
)}
</div>
</div>
);
};