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.
This commit is contained in:
159
components/AIInsightsWidget.tsx
Normal file
159
components/AIInsightsWidget.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user