'use client'; import * as React from "react"; import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, } from "@tanstack/react-table"; import { useVirtualizer } from "@tanstack/react-virtual"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from "@/components/ui/select"; import { Download, Filter, X } from "lucide-react"; import * as XLSX from 'xlsx'; interface AnaliticoItem { codigo_grupo: string; codigo_subgrupo: string; codigo_fornecedor: string; nome_fornecedor: string; id: number; codfilial: string; recnum: number; data_competencia: string; data_vencimento: string; data_pagamento: string; data_caixa: string; codigo_conta: string; conta: string; codigo_centrocusto: string; valor: number; historico: string; historico2: string; created_at: string; updated_at: string; } interface AnaliticoProps { filtros: { dataInicio: string; dataFim: string; centroCusto?: string; codigoGrupo?: string; codigoSubgrupo?: string; codigoConta?: string; }; } export default function AnaliticoComponent({ filtros }: AnaliticoProps) { const [data, setData] = React.useState([]); const [loading, setLoading] = React.useState(false); const [globalFilter, setGlobalFilter] = React.useState(""); const [columnFilters, setColumnFilters] = React.useState([]); const [open, setOpen] = React.useState(false); const [conditions, setConditions] = React.useState([{ column: "", operator: "contains", value: "" }]); const fetchData = React.useCallback(async () => { // Só faz a requisição se tiver dataInicio e dataFim if (!filtros.dataInicio || !filtros.dataFim) { setData([]); return; } setLoading(true); try { const params = new URLSearchParams({ dataInicio: filtros.dataInicio, dataFim: filtros.dataFim, ...(filtros.centroCusto && { centroCusto: filtros.centroCusto }), ...(filtros.codigoGrupo && { codigoGrupo: filtros.codigoGrupo }), ...(filtros.codigoSubgrupo && { codigoSubgrupo: filtros.codigoSubgrupo, }), ...(filtros.codigoConta && { codigoConta: filtros.codigoConta }), }); const response = await fetch(`/api/analitico?${params}`); if (response.ok) { const result = await response.json(); setData(result as AnaliticoItem[]); } else { console.error('Erro ao buscar dados:', await response.text()); } } catch (error) { console.error('Erro ao buscar dados:', error); } finally { setLoading(false); } }, [filtros]); React.useEffect(() => { fetchData(); }, [fetchData]); const columns = React.useMemo( () => [ { accessorKey: "data_competencia", header: "Data Comp.", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString('pt-BR'); } }, { accessorKey: "data_vencimento", header: "Data Venc.", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString('pt-BR'); } }, { accessorKey: "data_caixa", header: "Data Caixa", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString('pt-BR'); } }, { accessorKey: "codigo_fornecedor", header: "Cód. Fornec.", filterFn: "advancedText" }, { accessorKey: "nome_fornecedor", header: "Fornecedor", filterFn: "advancedText" }, { accessorKey: "codigo_centrocusto", header: "Cód. Centro", filterFn: "advancedText" }, { accessorKey: "codigo_conta", header: "Cód. Conta", filterFn: "advancedText" }, { accessorKey: "conta", header: "Conta", filterFn: "advancedText" }, { accessorKey: "valor", header: "Valor", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); const formatted = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', }).format(value); const isNegative = value < 0; return ( {formatted} ); } }, { accessorKey: "historico", header: "Histórico", filterFn: "advancedText" }, { accessorKey: "historico2", header: "Histórico 2", filterFn: "advancedText" }, { accessorKey: "recnum", header: "Recnum", filterFn: "advancedText" }, ], [] ); const filterFns = React.useMemo( () => ({ advancedText: (row: any, columnId: string, filters: any[]) => { if (!filters || filters.length === 0) return true; // Se veio um único filtro (objeto), transforma em array const conds = Array.isArray(filters) ? filters : [filters]; // A coluna deve atender a todas as condições aplicáveis a ela return conds.every((filter) => { const raw = row.getValue(columnId); const v = raw == null ? "" : String(raw); const op = filter.operator; const q = (filter.value ?? "").toString(); const a = v.toLowerCase(); const b = q.toLowerCase(); switch (op) { case "contains": return a.includes(b); case "equals": return a === b; case "startsWith": return a.startsWith(b); case "endsWith": return a.endsWith(b); case "empty": return a.length === 0; case "notEmpty": return a.length > 0; default: return true; } }); }, }), [] ); const table = useReactTable({ data, columns, state: { globalFilter, columnFilters }, onGlobalFilterChange: setGlobalFilter, onColumnFiltersChange: setColumnFilters, filterFns, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), }); const parentRef = React.useRef(null); const rowVirtualizer = useVirtualizer({ count: table.getRowModel().rows.length, getScrollElement: () => parentRef.current, estimateSize: () => 36, overscan: 20, }); const virtualRows = rowVirtualizer.getVirtualItems(); const applyFilters = () => { // Agrupar múltiplas condições por coluna const grouped: Record = {}; conditions.forEach((c) => { if ( c.column && (c.operator === "empty" || c.operator === "notEmpty" || (c.value ?? "") !== "") ) { if (!grouped[c.column]) grouped[c.column] = []; grouped[c.column].push({ operator: c.operator, value: c.value }); } }); // Converte em formato aceito pelo TanStack const filters = Object.keys(grouped).map((col) => ({ id: col, value: grouped[col], })); setColumnFilters(filters); setOpen(false); }; const clearFilters = () => { setConditions([{ column: "", operator: "contains", value: "" }]); setColumnFilters([]); setGlobalFilter(""); }; const totalValor = data.reduce((sum, item) => { const valor = typeof item.valor === 'string' ? parseFloat(item.valor) : item.valor; return sum + (isNaN(valor) ? 0 : valor); }, 0); const exportToExcel = () => { if (data.length === 0) return; // Preparar dados para exportação const exportData = data.map((item) => ({ 'Data Competência': new Date(item.data_competencia).toLocaleDateString( 'pt-BR' ), 'Data Vencimento': new Date(item.data_vencimento).toLocaleDateString( 'pt-BR' ), 'Data Caixa': new Date(item.data_caixa).toLocaleDateString('pt-BR'), 'Código Fornecedor': item.codigo_fornecedor, Fornecedor: item.nome_fornecedor, 'Código Centro Custo': item.codigo_centrocusto, 'Centro Custo': item.codigo_centrocusto, // Assumindo que é o mesmo valor 'Código Conta': item.codigo_conta, Conta: item.conta, Valor: typeof item.valor === 'string' ? parseFloat(item.valor) : item.valor, Histórico: item.historico, 'Histórico 2': item.historico2, Recnum: item.recnum, })); // Criar workbook const wb = XLSX.utils.book_new(); const ws = XLSX.utils.json_to_sheet(exportData); // Adicionar resumo na segunda aba const resumoData = [ { Métrica: 'Total de Registros', Valor: data.length }, { Métrica: 'Valor Total', Valor: totalValor }, ]; const wsResumo = XLSX.utils.json_to_sheet(resumoData); // Adicionar abas ao workbook XLSX.utils.book_append_sheet(wb, ws, 'Dados Analíticos'); XLSX.utils.book_append_sheet(wb, wsResumo, 'Resumo'); // Gerar nome do arquivo com data e hora const now = new Date(); const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-'); const fileName = `analitico_${timestamp}.xlsx`; // Fazer download XLSX.writeFile(wb, fileName); }; return (
{/* Header Section */}

Análise Analítica

Relatório detalhado de transações

{/* Controls */}
) => setGlobalFilter(e.target.value)} className="w-64 bg-white border-gray-300 focus:border-blue-500 focus:ring-blue-500" /> {(columnFilters.length > 0 || globalFilter) && ( )} {data.length > 0 && ( )}
{/* Table Container */}
{/* Table Header */}
Data Comp.
Data Venc.
Data Caixa
Cód. Fornec.
Fornecedor
Cód. Centro
Cód. Conta
Conta
Valor
Recnum
{/* Table Body */}
{loading ? (

Carregando dados...

) : virtualRows.length === 0 ? (

Nenhum dado encontrado

) : (
{virtualRows.map((virtualRow) => { const row = table.getRowModel().rows[virtualRow.index]; return (
{new Date(row.original.data_competencia).toLocaleDateString('pt-BR')}
{new Date(row.original.data_vencimento).toLocaleDateString('pt-BR')}
{new Date(row.original.data_caixa).toLocaleDateString('pt-BR')}
{row.original.codigo_fornecedor}
{row.original.nome_fornecedor}
{row.original.codigo_centrocusto}
{row.original.codigo_conta}
{row.original.conta}
{new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(row.original.valor)}
{row.original.recnum}
); })}
)}
{/* Summary Footer - Integrado */} {data.length > 0 && (

Total de Registros: {table.getRowModel().rows.length}

Transações encontradas

Valor Total: {new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', }).format(totalValor)}

Soma de todos os valores

)}
{/* Advanced Filters Dialog */} Filtros Avançados
{conditions.map((cond, idx) => (
{!(cond.operator === "empty" || cond.operator === "notEmpty") && (
) => { const next = [...conditions]; next[idx].value = e.target.value; setConditions(next); }} placeholder="Digite o valor" className="w-full bg-white border-gray-300" />
)} {conditions.length > 1 && (
)}
))}
); }