testes/src_2/features/financeiro-cnab/views/DashboardView.jsx

331 lines
14 KiB
JavaScript

import React from 'react';
import {
Users,
FileText,
ArrowUpRight,
ArrowDownRight,
DollarSign,
Activity,
Loader2,
Download
} from 'lucide-react';
import {
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
AreaChart,
Area,
PieChart,
Pie,
Cell
} from 'recharts';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
import { useCnabDashboard } from '../hooks/useCnabDashboard';
import RemessaDetailsModal from '../components/RemessaDetailsModal';
import { useCnabStore } from '../hooks/useCnabStore';
import CnabExcelTable from '../components/CnabExcelTable';
const COLORS = ['#4ade80', '#fbbf24', '#3b82f6', '#a855f7', '#ec4899'];
/**
* Cartão de Estatística (KPI) Premium
*/
const StatCard = ({ title, value, icon: Icon, trend, detail }) => (
<motion.div
whileHover={{ y: -5 }}
className="bg-white dark:bg-[#1b1b1b] border border-zinc-200 dark:border-zinc-800 rounded-3xl p-6 shadow-sm flex flex-col justify-between group hover:border-emerald-500/30 transition-all relative overflow-hidden"
>
{/* Background Decorative Element */}
<div className="absolute -right-4 -bottom-4 opacity-[0.03] dark:opacity-[0.05] group-hover:scale-110 transition-transform duration-700">
<Icon size={120} />
</div>
<div className="flex justify-between items-start mb-4">
<div className="p-3 bg-emerald-50 dark:bg-emerald-500/10 rounded-2xl text-emerald-600 dark:text-emerald-500 border border-emerald-100 dark:border-emerald-500/20 shadow-inner">
<Icon size={24} />
</div>
{trend && (
<div className={cn(
"flex items-center gap-1 px-2 py-1 rounded-full text-[10px] font-black",
trend > 0 ? "bg-emerald-500/10 text-emerald-500" : "bg-rose-500/10 text-rose-500"
)}>
{trend > 0 ? <ArrowUpRight size={12} /> : <ArrowDownRight size={12} />}
{Math.abs(trend)}%
</div>
)}
</div>
<div>
<h3 className="text-zinc-500 dark:text-zinc-500 text-xs font-black uppercase tracking-widest mb-1">{title}</h3>
<p className="text-2xl font-black text-slate-900 dark:text-white tracking-tighter truncate">{value}</p>
<p className="text-[10px] text-zinc-400 dark:text-zinc-600 font-medium mt-1">{detail}</p>
</div>
</motion.div>
);
/**
* Dashboard principal do módulo CNAB
*/
const DashboardView = () => {
const { paymentMode } = useCnabStore();
const {
data,
loading,
selectedRemessa,
setSelectedRemessa,
remessaDetails,
loadingDetails,
fetchRemessaDetails,
exportRemessa
} = useCnabDashboard();
if (loading || !data) {
return (
<div className="h-full flex flex-col items-center justify-center space-y-4 animate-in fade-in duration-700">
<div className="relative">
<div className="w-16 h-16 rounded-3xl bg-emerald-500/10 border border-emerald-500/20 animate-spin flex items-center justify-center">
<Loader2 className="text-emerald-500 w-8 h-8" />
</div>
</div>
<p className="text-zinc-600 text-xs font-black uppercase tracking-[0.3em]">Sincronizando Dados...</p>
</div>
);
}
const ICON_MAP = {
'Total Favorecidos': Users,
'Valor Total': DollarSign,
'Qtd. Remessas': FileText,
};
// Colunas para a tabela de remessas recentes
const remessaColumns = [
{ field: 'id', header: 'Nº Remessa', width: '120px', render: (row) => <span className="font-bold text-emerald-500">{row.id}</span> },
{ field: 'qtd', header: 'Quantidade', width: '120px' },
{ field: 'valor', header: 'Total', width: '150px', render: (row) => <span className="font-mono font-bold text-white">{row.valor}</span> },
{ field: 'data', header: 'Data Pagamento', width: '150px' },
{ field: 'status', header: 'Status', width: '120px', render: (row) => (
<span className="px-2 py-0.5 rounded text-[10px] font-black uppercase bg-emerald-500/10 text-emerald-500 border border-emerald-500/20">
{row.status}
</span>
)},
{
field: 'id',
header: 'Ações',
width: '120px',
render: (row) => (
<div className="flex gap-2">
<button
onClick={() => exportRemessa(row.id)}
className="p-1.5 hover:bg-zinc-800 rounded transition-colors"
title="arquivo cnab"
>
<FileText size={16} className="text-zinc-500 hover:text-white" />
</button>
<button
onClick={() => fetchRemessaDetails(row.id)}
className="p-1.5 hover:bg-zinc-800 rounded transition-colors"
title="detalhamento cnab"
>
<DollarSign size={16} className="text-zinc-500 hover:text-emerald-500" />
</button>
</div>
)
}
];
return (
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 h-full flex flex-col overflow-auto custom-scrollbar pr-2 pb-10">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6 shrink-0">
<div>
<div className="flex items-center gap-3 mb-2">
<div className="p-2 bg-emerald-500/10 rounded-lg text-emerald-500">
<Activity size={20} />
</div>
<h2 className="text-3xl font-black text-slate-900 dark:text-white tracking-tighter">Controle de <span className="text-emerald-500">Remessas</span></h2>
</div>
<p className="text-zinc-500 text-sm font-medium">Monitoramento em tempo real de arquivos e favorecidos em modo <span className="text-emerald-500 font-bold">{paymentMode}</span></p>
</div>
<div className="flex flex-wrap gap-2">
<button className="group flex items-center gap-2 px-5 py-2.5 bg-white dark:bg-zinc-900 hover:bg-zinc-50 dark:hover:bg-zinc-800 text-zinc-600 dark:text-zinc-400 rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all border border-zinc-200 dark:border-zinc-800 shadow-sm active:scale-95">
<Activity size={16} className="group-hover:text-emerald-500 transition-colors" />
Análise Gráfica
</button>
<button className="group flex items-center gap-2 px-5 py-2.5 bg-white dark:bg-zinc-900 hover:bg-zinc-50 dark:hover:bg-zinc-800 text-zinc-600 dark:text-zinc-400 rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all border border-zinc-200 dark:border-zinc-800 shadow-sm active:scale-95">
<FileText size={16} className="group-hover:text-amber-500 transition-colors" />
Relatórios
</button>
<button className="group flex items-center gap-2 px-6 py-2.5 bg-emerald-600 hover:bg-emerald-500 text-white rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all shadow-xl shadow-emerald-900/20 active:scale-95 border border-emerald-500/20">
<Download size={16} />
Exportar Painel
</button>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 shrink-0">
{data.stats.map((stat, idx) => (
<StatCard
key={idx}
{...stat}
icon={ICON_MAP[stat.title] || Activity}
/>
))}
</div>
{/* Charts Section */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 min-h-[400px]">
{/* Main Bar Chart */}
<div className="bg-white dark:bg-[#1b1b1b] border border-zinc-200 dark:border-zinc-800 rounded-[2.5rem] p-8 shadow-sm flex flex-col group overflow-hidden relative">
<div className="absolute top-0 right-0 w-32 h-32 bg-emerald-500/5 blur-3xl rounded-full" />
<div className="flex justify-between items-center mb-8 relative">
<h3 className="text-lg font-bold text-slate-900 dark:text-white flex items-center gap-2">
<DollarSign size={18} className="text-emerald-500" />
Volume Financeiro Mensal
</h3>
<select className="bg-zinc-100 dark:bg-zinc-900 border-none rounded-lg text-xs font-bold text-zinc-500 px-3 py-1.5 outline-none cursor-pointer">
<option>Últimos 6 meses</option>
<option>2025</option>
</select>
</div>
<div className="flex-1 min-h-[300px]">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={data.chartData}>
<defs>
<linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#10b981" stopOpacity={0.3}/>
<stop offset="95%" stopColor="#10b981" stopOpacity={0}/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" dark:stroke="#2a2a2a" vertical={false} />
<XAxis
dataKey="name"
stroke="#64748b"
fontSize={10}
fontWeight="bold"
tickLine={false}
axisLine={false}
dy={10}
/>
<YAxis
stroke="#64748b"
fontSize={10}
fontWeight="bold"
tickLine={false}
axisLine={false}
tickFormatter={(value) => `R$ ${(value / 1000000).toFixed(1)}M`}
/>
<Tooltip
contentStyle={{ backgroundColor: '#18181b', border: '1px solid #27272a', borderRadius: '12px', color: '#fff' }}
itemStyle={{ color: '#10b981' }}
cursor={{stroke: '#10b981', strokeWidth: 1}}
/>
<Area
type="monotone"
dataKey="valor"
stroke="#10b981"
strokeWidth={3}
fillOpacity={1}
fill="url(#colorValue)"
animationDuration={2000}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
{/* Pie Chart */}
<div className="bg-white dark:bg-[#1b1b1b] border border-zinc-200 dark:border-zinc-800 rounded-[2.5rem] p-8 shadow-sm flex flex-col relative overflow-hidden">
<div className="absolute bottom-0 left-0 w-32 h-32 bg-emerald-500/5 blur-3xl rounded-full" />
<h3 className="text-lg font-bold text-slate-900 dark:text-white mb-8 flex items-center gap-2 relative">
<Activity size={18} className="text-emerald-500" />
Distribuição por Modalidade
</h3>
<div className="flex-1 min-h-[300px] flex flex-col md:flex-row items-center justify-center gap-8">
<div className="flex-1 w-full">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={data.pieData}
cx="50%"
cy="50%"
innerRadius={70}
outerRadius={100}
paddingAngle={8}
dataKey="value"
animationDuration={2500}
>
{data.pieData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[index % COLORS.length]}
stroke="rgba(0,0,0,0)"
strokeWidth={0}
/>
))}
</Pie>
<Tooltip
contentStyle={{ backgroundColor: '#18181b', border: '1px solid #27272a', borderRadius: '12px', color: '#fff' }}
/>
</PieChart>
</ResponsiveContainer>
</div>
{/* Legend */}
<div className="flex flex-col gap-3 relative">
{data.pieData.map((item, idx) => (
<div key={idx} className="flex items-center gap-3">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: COLORS[idx % COLORS.length] }} />
<div>
<p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest leading-none">{item.name}</p>
<p className="text-sm font-bold text-white leading-tight">{item.value}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* Recent Remessas Table Area */}
<div className="flex flex-col flex-1 min-h-[400px]">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-bold text-slate-900 dark:text-white tracking-tight flex items-center gap-2">
<FileText size={18} className="text-emerald-500" />
Arquivos Gerados Recentes
</h3>
<button className="text-xs font-bold text-zinc-500 hover:text-emerald-500 transition-colors uppercase tracking-widest">Ver todos arquivos</button>
</div>
<div className="flex-1">
<CnabExcelTable
data={data.recentRemessas}
columns={remessaColumns}
pageSize={5}
rowKey="id"
/>
</div>
</div>
{/* Details Modal */}
<RemessaDetailsModal
isOpen={!!selectedRemessa}
onClose={() => setSelectedRemessa(null)}
remessaId={selectedRemessa}
details={remessaDetails}
loading={loadingDetails}
/>
</div>
);
};
export default DashboardView;