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.
159 lines
6.4 KiB
TypeScript
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>
|
|
);
|
|
}; |