import { useState, useEffect, useCallback } from 'react'
import { motion } from 'framer-motion'
import { Link } from 'react-router-dom'
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend,
PieChart, Pie, Cell,
} from 'recharts'
import {
Zap,
RefreshCw,
ArrowLeft,
TrendingUp,
TrendingDown,
DollarSign,
Receipt,
PiggyBank,
Activity,
LayoutDashboard,
} from 'lucide-react'
interface FinancialData {
vendas_mes: number
vendas_ano: number
despesas_mes: number
despesas_ano: number
lucro_mes: number
lucro_ano: number
categorias: { name: string; value: number }[]
evolucao: { mes: string; receita: number; despesa: number }[]
}
const containerVariants = {
hidden: { opacity: 0 },
show: { opacity: 1, transition: { staggerChildren: 0.05 } }
}
const itemVariants = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 }
}
const PIE_COLORS = [
'#10b981', '#8b5cf6', '#f59e0b', '#3b82f6', '#ef4444',
'#06b6d4', '#ec4899', '#84cc16', '#f97316', '#6366f1',
'#14b8a6', '#e879f9', '#a3e635', '#fb923c', '#818cf8',
]
const formatEUR = (v: number) => `${v.toLocaleString('pt-PT', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}€`
const formatMesLabel = (mes: string) => {
const [, m] = mes.split('-')
const nomes = ['', 'Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
return nomes[parseInt(m)] || mes
}
const StatCard = ({ label, value, icon: Icon, color, sub }: {
label: string; value: string; icon: React.ElementType; color: string; sub?: string
}) => (
{value}
{sub && {sub}
}
)
const CustomTooltip = ({ active, payload, label }: any) => {
if (!active || !payload?.length) return null
return (
{label}
{payload.map((p: any, i: number) => (
{p.name}: {formatEUR(p.value)}
))}
)
}
export default function Financial() {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
const fetchData = useCallback(async () => {
setRefreshing(true)
try {
const response = await fetch('/api/financial')
if (!response.ok) throw new Error('Failed')
const json = await response.json()
setData(json)
} catch {
console.error('Failed to fetch financial data')
} finally {
setLoading(false)
setRefreshing(false)
}
}, [])
useEffect(() => { fetchData() }, [fetchData])
if (loading) {
return (
A carregar dados financeiros...
)
}
if (!data) return null
const lucroColor = data.lucro_ano >= 0 ? 'text-emerald-400 bg-emerald-500/20' : 'text-red-400 bg-red-500/20'
const lucroLabel = data.lucro_ano >= 0 ? 'Lucro' : 'Prejuizo'
// Format evolution data for chart
const chartData = data.evolucao.map(e => ({
mes: formatMesLabel(e.mes),
Receita: e.receita,
Despesa: e.despesa,
}))
return (
{/* Header */}
Financeiro
Vendas e Despesas {new Date().getFullYear()}
{lucroLabel}: {formatEUR(Math.abs(data.lucro_ano))}
Monitor
Dashboard
{/* Main Content */}
{/* Summary Cards */}
{/* Lucro cards */}
= 0 ? 'text-emerald-400' : 'text-red-400'}`} />
Resultado Mensal
= 0 ? 'text-emerald-400' : 'text-red-400'}`}>
{data.lucro_mes >= 0 ? '+' : ''}{formatEUR(data.lucro_mes)}
= 0 ? 'text-emerald-400' : 'text-red-400'}`} />
Resultado Anual
= 0 ? 'text-emerald-400' : 'text-red-400'}`}>
{data.lucro_ano >= 0 ? '+' : ''}{formatEUR(data.lucro_ano)}
{/* Charts */}
{/* Bar Chart - Monthly Evolution */}
Evolucao Mensal
`${v}€`} />
} />
{/* Pie Chart - Expense Distribution */}
Despesas por Categoria
{data.categorias.slice(0, 10).map((_, i) => (
|
))}
formatEUR(value || 0)}
contentStyle={{ background: '#18181b', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 12, fontSize: 12 }}
itemStyle={{ color: '#e4e4e7' }}
/>
{/* Legend */}
{data.categorias.slice(0, 10).map((cat, i) => (
))}
)
}