"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_vencimento", header: "Data de Vencimento", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString("pt-BR"); }, }, { accessorKey: "data_caixa", header: "Data de Caixa", filterFn: "advancedText", cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString("pt-BR"); }, }, { accessorKey: "entidade", header: "Entidade", filterFn: "advancedText", cell: () => "-", }, { accessorKey: "codigo_fornecedor", header: "Código do Fornecedor", filterFn: "advancedText", }, { accessorKey: "nome_fornecedor", header: "Nome do Fornecedor", filterFn: "advancedText", }, { accessorKey: "codigo_centrocusto", header: "Centro de Custo", filterFn: "advancedText", }, { accessorKey: "codigo_conta", header: "Código da Conta", filterFn: "advancedText", }, { accessorKey: "conta", header: "Nome da Conta", filterFn: "advancedText" }, { accessorKey: "valor", header: "Valor Realizado", 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: "valor_previsto", header: "Valor Previsto", filterFn: "advancedText", cell: () => "-", }, { accessorKey: "valor_confirmado", header: "Valor Confirmado", filterFn: "advancedText", cell: () => "-", }, { accessorKey: "valor_pago", header: "Valor Pago", filterFn: "advancedText", cell: () => "-", }, { accessorKey: "historico", header: "Histórico", filterFn: "advancedText", }, { accessorKey: "historico2", header: "Histórico 2", filterFn: "advancedText", }, { accessorKey: "numero_lancamento", header: "Número do Lançamento", filterFn: "advancedText", cell: () => "-", }, ], [] ); 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, setTotalValor] = React.useState(0); React.useEffect(() => { // Usar dados filtrados da tabela em vez dos dados originais const filteredData = table.getRowModel().rows.map(row => row.original); const newTotal = filteredData.reduce((sum, item) => { const valor = typeof item.valor === "string" ? parseFloat(item.valor) : item.valor; return sum + (isNaN(valor) ? 0 : valor); }, 0); console.log('🔄 Calculando total:', { totalRows: table.getRowModel().rows.length, originalDataLength: data.length, newTotal, columnFilters: columnFilters.length, globalFilter }); setTotalValor(newTotal); }, [table, data, columnFilters, globalFilter]); const exportToExcel = () => { if (data.length === 0) return; // Usar dados filtrados da tabela em vez dos dados originais const filteredData = table.getRowModel().rows.map(row => row.original); if (filteredData.length === 0) { alert('Nenhum dado filtrado para exportar'); return; } // Preparar dados para exportação const exportData = filteredData.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: filteredData.length }, { Métrica: "Valor Total", Valor: totalValor }, { Métrica: "Filtros Aplicados", Valor: columnFilters.length > 0 || globalFilter ? "Sim" : "Não" }, ]; 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 hasFilters = columnFilters.length > 0 || globalFilter; const fileName = `analitico${hasFilters ? '_filtrado' : ''}_${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 de Vencimento
Data de Caixa
Entidade
Código do Fornecedor
Nome do Fornecedor
Centro de Custo
Código da Conta
Nome da Conta
Valor Realizado
Valor Previsto
Valor Confirmado
Valor Pago
Histórico
Histórico 2
Número do Lançamento
{/* 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_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.historico}
{row.original.historico2}
-
); })}
)}
{/* 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 && (
)}
))}
); }