From 69a66a6cab60c03ebf9ace41c5910881df7c4a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 21 Oct 2025 17:28:50 -0300 Subject: [PATCH 01/14] fix: ajuste nos filtros gerais --- package-lock.json | 31 +++++ package.json | 1 + src/app/DRE/teste.tsx | 206 ++++++++++++++++++++++++++------- src/components/ui/checkbox.tsx | 30 +++++ 4 files changed, 225 insertions(+), 43 deletions(-) create mode 100644 src/components/ui/checkbox.tsx diff --git a/package-lock.json b/package-lock.json index 7e99986..ec0efb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "dre-gerencial", "version": "0.1.0", "dependencies": { + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", @@ -1536,6 +1537,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", diff --git a/package.json b/package.json index a5a9ebb..e9dd4d8 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index f3fd760..82daec0 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -6,6 +6,7 @@ import AnaliticoComponent from "./analitico"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Checkbox } from "@/components/ui/checkbox"; import { Sheet, SheetContent, @@ -77,6 +78,10 @@ export default function Teste() { valorMax: "", buscaTextual: "" }); + + // Estados para multi-seleção + const [centrosCustoSelecionados, setCentrosCustoSelecionados] = useState([]); + const [contasSelecionadas, setContasSelecionadas] = useState([]); const [isFilterOpen, setIsFilterOpen] = useState(false); const [dadosFiltrados, setDadosFiltrados] = useState([]); const [filtrosAplicados, setFiltrosAplicados] = useState(false); @@ -144,6 +149,10 @@ export default function Teste() { const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; setOpcoesContas(contasUnicas); + // Inicializar com todos os itens selecionados + setCentrosCustoSelecionados(centrosCustoUnicos); + setContasSelecionadas(contasUnicas); + } catch (error) { console.error("Erro ao carregar períodos:", error); } @@ -304,6 +313,43 @@ export default function Teste() { })); }; + // Funções para multi-seleção + const toggleCentroCusto = (centro: string) => { + setCentrosCustoSelecionados(prev => { + if (prev.includes(centro)) { + return prev.filter(c => c !== centro); + } else { + return [...prev, centro]; + } + }); + }; + + const toggleConta = (conta: string) => { + setContasSelecionadas(prev => { + if (prev.includes(conta)) { + return prev.filter(c => c !== conta); + } else { + return [...prev, conta]; + } + }); + }; + + const selecionarTodosCentros = () => { + setCentrosCustoSelecionados(opcoesCentrosCusto); + }; + + const limparCentros = () => { + setCentrosCustoSelecionados([]); + }; + + const selecionarTodasContas = () => { + setContasSelecionadas(opcoesContas); + }; + + const limparContas = () => { + setContasSelecionadas([]); + }; + const limparFiltros = () => { const agora = new Date(); const anoAtual = agora.getFullYear(); @@ -322,11 +368,18 @@ export default function Teste() { buscaTextual: "" }); + // Limpar multi-seleções + setCentrosCustoSelecionados([]); + setContasSelecionadas([]); + // Limpar dados da tabela setData([]); setDadosFiltrados([]); setFiltrosAplicados(false); setMesesDisponiveis([]); + + // Recarregar opções e selecionar todos novamente + carregarPeriodosDisponiveis(); }; const aplicarFiltros = async () => { @@ -372,17 +425,17 @@ export default function Teste() { ); } - // Filtro por centro de custo - if (filtros.centroCusto !== "Todos") { + // Filtro por centro de custo (multi-seleção) + if (centrosCustoSelecionados.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => - item.centro_custo === filtros.centroCusto + centrosCustoSelecionados.includes(item.centro_custo) ); } - // Filtro por conta - if (filtros.conta !== "Todas") { + // Filtro por conta (multi-seleção) + if (contasSelecionadas.length > 0) { dadosFiltrados = dadosFiltrados.filter((item: DREItem) => - item.conta === filtros.conta + contasSelecionadas.includes(item.conta) ); } @@ -496,10 +549,10 @@ export default function Teste() { // Agrupar por grupo const grupos = data.reduce((acc, item) => { - if (!acc[item.grupo]) { - acc[item.grupo] = []; - } - acc[item.grupo].push(item); + if (!acc[item.grupo]) { + acc[item.grupo] = []; + } + acc[item.grupo].push(item); return acc; }, {} as Record); @@ -739,7 +792,8 @@ export default function Teste() { {row.grupo} {isCalculado && ( - + // + <> )} @@ -818,7 +872,7 @@ export default function Teste() { const hierarchicalData = buildHierarchicalData(); - return ( + return (
{/* Header Section */}
@@ -829,8 +883,8 @@ export default function Teste() {

Demonstração do Resultado do Exercício

-
+ {/* Botão de Filtro */} @@ -865,7 +919,7 @@ export default function Teste() { ))} - +
-
+ @@ -914,37 +968,103 @@ export default function Teste() { */} - {/* Centro de Custo + {/* Centro de Custo */}
- - -
*/} +
+ +
+ + +
+
+
+ {opcoesCentrosCusto.map(centro => ( +
+ toggleCentroCusto(centro)} + /> + +
+ ))} +
+ {centrosCustoSelecionados.length > 0 && ( +
+ {centrosCustoSelecionados.length} centro(s) selecionado(s) +
+ )} + - {/* Conta + {/* Conta */}
- - -
*/} +
+ +
+ + +
+
+
+ {opcoesContas.map(conta => ( +
+ toggleConta(conta)} + /> + +
+ ))} +
+ {contasSelecionadas.length > 0 && ( +
+ {contasSelecionadas.length} conta(s) selecionada(s) +
+ )} + {/* Valor
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..df61a13 --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } From 461f8966ab4c3beac9c0d1e6d89fb3ba12ab1293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 21 Oct 2025 17:57:58 -0300 Subject: [PATCH 02/14] fix:linha calculado bg --- src/app/DRE/analitico.tsx | 3 +- src/app/DRE/teste.tsx | 72 ++++++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/app/DRE/analitico.tsx b/src/app/DRE/analitico.tsx index ef6d98f..6113ecd 100644 --- a/src/app/DRE/analitico.tsx +++ b/src/app/DRE/analitico.tsx @@ -67,6 +67,7 @@ interface AnaliticoProps { codigoGrupo?: string; codigoSubgrupo?: string; codigoConta?: string; + linhaSelecionada?: string; // Adicionar propriedade para linha selecionada }; } @@ -563,7 +564,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
*/}

- Análise Analítica + Análise Analítica{filtros.linhaSelecionada ? ` - ${filtros.linhaSelecionada}` : ""}

Relatório detalhado de transações diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 82daec0..b150af3 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -100,6 +100,7 @@ export default function Teste() { codigoGrupo: "", codigoSubgrupo: "", codigoConta: "", + linhaSelecionada: "", // Adicionar informação da linha selecionada }); const [linhaSelecionada, setLinhaSelecionada] = useState(null); @@ -270,6 +271,7 @@ export default function Teste() { codigoGrupo, codigoSubgrupo, codigoConta: row.codigo_conta?.toString() || "", + linhaSelecionada: row.grupo || row.subgrupo || row.centro_custo || row.conta || "", // Incluir informação da linha selecionada }; console.log('🎯 Novos filtros para analítico:', novosFiltros); @@ -740,6 +742,9 @@ export default function Teste() { }-${row.codigo_conta || ""}`; const isSelected = linhaSelecionada === linhaId; + // Verificar se é um grupo calculado + const isCalculado = row.isCalculado === true; + let style = baseStyle; if (isSelected) { @@ -749,6 +754,10 @@ export default function Teste() { switch (row.type) { case "grupo": + if (isCalculado) { + // Destacar grupos calculados com cor mais vibrante + return `${style} bg-gradient-to-r from-amber-100/80 to-yellow-100/80 font-bold text-gray-900 border-b-2 border-amber-300 shadow-sm`; + } return `${style} bg-gradient-to-r from-blue-50/20 to-indigo-50/20 font-bold text-gray-900 border-b-2 border-blue-200`; case "subgrupo": return `${style} bg-gradient-to-r from-gray-50/30 to-blue-50/20 font-semibold text-gray-800`; @@ -1041,7 +1050,7 @@ export default function Teste() { Limpar

- +
{opcoesContas.map(conta => (
@@ -1193,17 +1202,62 @@ export default function Teste() {
{/* Table Header */}
-
-
Descrição
- {mesesDisponiveis.map((mes) => ( -
-
{mes}
-
+
+
+
Descrição
+ {mesesDisponiveis.map((mes) => ( +
+
{mes}
+
%
))} -
Total
+
Total
+
+ + {/* Botões de controle */} +
+ + +
@@ -1212,7 +1266,7 @@ export default function Teste() { {hierarchicalData.map((row, index) => (
From 1375a471a2cb512acfdf4b35580cad25de92b990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 21 Oct 2025 18:18:51 -0300 Subject: [PATCH 03/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=B5es=20do=20analiti?= =?UTF-8?q?co?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/analitico.tsx | 200 ++++++++++++++++++-------------------- src/app/DRE/teste.tsx | 96 +++++++++--------- 2 files changed, 141 insertions(+), 155 deletions(-) diff --git a/src/app/DRE/analitico.tsx b/src/app/DRE/analitico.tsx index 6113ecd..e9c425c 100644 --- a/src/app/DRE/analitico.tsx +++ b/src/app/DRE/analitico.tsx @@ -543,25 +543,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { }; return ( -
+
{/* Header Section */} -
-
- {/*
- - - -
*/} +
+

Análise Analítica{filtros.linhaSelecionada ? ` - ${filtros.linhaSelecionada}` : ""} @@ -570,12 +555,58 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { 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 && ( + + )} +
+
{/* Filtros Externos Ativos */} {(filtrosExternos.dataInicio || filtrosExternos.centroCusto || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( -
-
+
+
Filtros aplicados pela tabela DRE Gerencial:
@@ -583,72 +614,27 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {filtrosExternos.dataInicio && filtrosExternos.dataFim && ( Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim} - - )} + + )} {filtrosExternos.centroCusto && ( Centro: {filtrosExternos.centroCusto} - - )} + + )} {filtrosExternos.codigoGrupo && ( Grupo: {filtrosExternos.codigoGrupo} - - )} + + )} {filtrosExternos.codigoConta && ( Conta: {filtrosExternos.codigoConta} - - )} -
-
- )} - - {/* 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 */} @@ -659,36 +645,36 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
Data de Vencimento -
+
Data de Caixa
Entidade
Cód. Fornec -
+
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 -
+
@@ -701,7 +687,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { scrollbarColor: "#cbd5e0 #f7fafc", }} > - {loading ? ( + {loading ? (
@@ -727,9 +713,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {

Nenhum dado encontrado

-
-
- ) : ( + + + ) : (
+
{new Date(row.original.data_caixa).toLocaleDateString( "pt-BR" )} -
+
{row.original.entidade || "-"} -
+
{row.original.codigo_fornecedor} -
+
{row.original.nome_fornecedor} -
+
{row.original.codigo_centrocusto} -
+
{row.original.codigo_conta} -
+
{row.original.conta} -
+
+ ) : ( - )} @@ -846,20 +832,20 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{row.original.numero_lancamento || "-"}
-
+ ); })} - )} - + )} + {/* Footer com Totalizador das Colunas */} - {data.length > 0 && ( + {data.length > 0 && (
TOTAL: {table.getRowModel().rows.length} registros -
+
@@ -920,14 +906,14 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { ) : ( - )} -
+
- - - )} + + + )} {/* Summary Footer - Integrado */} { @@ -988,7 +974,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {conditions.map((cond, idx) => (
- + */} @@ -1138,13 +1138,13 @@ export default function Teste() { {/* Loading quando aplicando filtros */} {loading && ( -
-
-
+
+
+
-

+

Aplicando filtros...

@@ -1157,9 +1157,9 @@ export default function Teste() { {/* Erro */} {error && !loading && ( -

-
-
+
+
+
@@ -1169,7 +1169,7 @@ export default function Teste() { Erro ao carregar dados

{error}

-
@@ -1179,16 +1179,16 @@ export default function Teste() { {/* Mensagem quando não há dados */} {!filtrosAplicados && !loading && !error && ( -
-
-
+
+
+
-

+

Nenhum dado exibido

-

+

Clique no botão "Filtros" para definir os critérios de busca e visualizar os dados do DRE.

@@ -1202,8 +1202,8 @@ export default function Teste() {
{/* Table Header */}
-
-
+
+
Descrição
{mesesDisponiveis.map((mes) => (
@@ -1217,7 +1217,7 @@ export default function Teste() {
{/* Botões de controle */} -
+
@@ -1253,7 +1253,7 @@ export default function Teste() { setExpandedSubgrupos(new Set()); setExpandedCentros(new Set()); }} - className="text-xs h-6 px-2" + className="text-xs h-5 px-2" > Recolher Tudo @@ -1266,7 +1266,7 @@ export default function Teste() { {hierarchicalData.map((row, index) => (
From 4226fa6c7661039ea449002d5fd8e88f956130ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 09:53:57 -0300 Subject: [PATCH 04/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20da=20ordem=20?= =?UTF-8?q?dos=20valores=20das=20colunas=20por=20periodo=20na=20tabela=20s?= =?UTF-8?q?int=C3=A9tica?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/analitico.tsx | 370 +++++++++++++++++--------------------- src/app/DRE/teste.tsx | 21 +-- 2 files changed, 168 insertions(+), 223 deletions(-) diff --git a/src/app/DRE/analitico.tsx b/src/app/DRE/analitico.tsx index e9c425c..716c3c1 100644 --- a/src/app/DRE/analitico.tsx +++ b/src/app/DRE/analitico.tsx @@ -7,6 +7,7 @@ import { getSortedRowModel, getFilteredRowModel, ColumnFiltersState, + ColumnSizingState, } from "@tanstack/react-table"; import { useVirtualizer } from "@tanstack/react-virtual"; import { Input } from "@/components/ui/input"; @@ -76,6 +77,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { const [loading, setLoading] = React.useState(false); const [globalFilter, setGlobalFilter] = React.useState(""); const [columnFilters, setColumnFilters] = React.useState([]); + const [columnSizing, setColumnSizing] = React.useState({}); const [open, setOpen] = React.useState(false); const [conditions, setConditions] = React.useState([ { column: "", operator: "contains", value: "" }, @@ -154,6 +156,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "data_vencimento", header: "Data de Vencimento", filterFn: "advancedText", + size: 140, + minSize: 100, + maxSize: 200, cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString("pt-BR"); @@ -163,6 +168,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "data_caixa", header: "Data de Caixa", filterFn: "advancedText", + size: 120, + minSize: 100, + maxSize: 180, cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return new Date(value).toLocaleDateString("pt-BR"); @@ -172,6 +180,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "entidade", header: "Entidade", filterFn: "advancedText", + size: 120, + minSize: 80, + maxSize: 200, cell: ({ getValue }: { getValue: () => string }) => { const value = getValue(); return value || "-"; @@ -181,31 +192,49 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "codigo_fornecedor", header: "Código do Fornecedor", filterFn: "advancedText", + size: 120, + minSize: 100, + maxSize: 150, }, { accessorKey: "nome_fornecedor", header: "Nome do Fornecedor", filterFn: "advancedText", + size: 220, + minSize: 150, + maxSize: 400, }, { accessorKey: "codigo_centrocusto", header: "Centro de Custo", filterFn: "advancedText", + size: 140, + minSize: 100, + maxSize: 200, }, { accessorKey: "codigo_conta", header: "Código da Conta", filterFn: "advancedText", + size: 130, + minSize: 100, + maxSize: 180, }, { accessorKey: "conta", header: "Nome da Conta", filterFn: "advancedText", + size: 160, + minSize: 120, + maxSize: 300, }, { accessorKey: "valor", header: "Valor Realizado", filterFn: "advancedText", + size: 130, + minSize: 100, + maxSize: 200, cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); const formatted = new Intl.NumberFormat("pt-BR", { @@ -224,6 +253,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "valor_previsto", header: "Valor Previsto", filterFn: "advancedText", + size: 120, + minSize: 100, + maxSize: 180, cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); if (!value || value === 0) return "-"; @@ -243,6 +275,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "valor_confirmado", header: "Valor Confirmado", filterFn: "advancedText", + size: 130, + minSize: 100, + maxSize: 200, cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); if (!value || value === 0) return "-"; @@ -262,6 +297,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "valor_pago", header: "Valor Pago", filterFn: "advancedText", + size: 140, + minSize: 100, + maxSize: 200, cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); if (!value || value === 0) return "-"; @@ -281,16 +319,25 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { accessorKey: "historico", header: "Histórico", filterFn: "advancedText", + size: 320, + minSize: 200, + maxSize: 500, }, { accessorKey: "historico2", header: "Histórico 2", filterFn: "advancedText", + size: 500, + minSize: 300, + maxSize: 800, }, { accessorKey: "numero_lancamento", header: "Número do Lançamento", filterFn: "advancedText", + size: 30, + minSize: 20, + maxSize: 50, cell: ({ getValue }: { getValue: () => number }) => { const value = getValue(); return value || "-"; @@ -342,13 +389,16 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { const table = useReactTable({ data, columns: columns as any, - state: { globalFilter, columnFilters }, + state: { globalFilter, columnFilters, columnSizing }, onGlobalFilterChange: setGlobalFilter, onColumnFiltersChange: setColumnFilters, + onColumnSizingChange: setColumnSizing, filterFns, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), + enableColumnResizing: true, + columnResizeMode: 'onChange', }); const parentRef = React.useRef(null); @@ -643,38 +693,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {/* Table Header */}
-
- Data de Vencimento + {table.getHeaderGroups().map(headerGroup => ( + headerGroup.headers.map(header => ( +
+ {header.isPlaceholder ? null : ( +
+ {String(header.column.columnDef.header)} + {header.column.getCanResize() && ( +
+ )}
-
Data de Caixa
-
Entidade
-
- Cód. Fornec -
-
- 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 + )}
+ )) + ))}
@@ -692,8 +732,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {

Carregando dados...

-
-
+
+
) : virtualRows.length === 0 ? (
@@ -711,10 +751,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> -
+

Nenhum dado encontrado

-
-
+
+
) : (
-
- {new Date( - row.original.data_vencimento - ).toLocaleDateString("pt-BR")} + {row.getVisibleCells().map(cell => ( +
+ {String(cell.renderValue() || '')}
-
- {new Date(row.original.data_caixa).toLocaleDateString( - "pt-BR" - )} + ))}
-
- {row.original.entidade || "-"} -
-
- {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.valor_previsto && row.original.valor_previsto !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(row.original.valor_previsto)} - - ) : ( - - - )} -
-
- {row.original.valor_confirmado && row.original.valor_confirmado !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(row.original.valor_confirmado)} - - ) : ( - - - )} -
-
- {row.original.valor_pago && row.original.valor_pago !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(row.original.valor_pago)} - - ) : ( - - - )} -
-
-
- {row.original.historico} -
-
- {row.original.historico2} -
-
- {row.original.numero_lancamento || "-"} -
-
); })}
)} -
+
{/* Footer com Totalizador das Colunas */} - {data.length > 0 && ( + {data.length > 0 && (
-
- TOTAL: {table.getRowModel().rows.length} registros -
-
-
-
-
-
-
-
-
- {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valorRealizado)} -
-
- {columnTotals.valorPrevisto !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valorPrevisto)} + {table.getHeaderGroups().map(headerGroup => ( + headerGroup.headers.map((header, index) => ( +
+ {index === 0 && ( + + TOTAL: {table.getRowModel().rows.length} registros + + )} + {index === 8 && ( + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valorRealizado)} + + )} + {index === 9 && ( + + {columnTotals.valorPrevisto !== 0 ? ( + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valorPrevisto)} + + ) : ( + - + )} + + )} + {index === 10 && ( + + {columnTotals.valorConfirmado !== 0 ? ( + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valorConfirmado)} + + ) : ( + - + )} + + )} + {index === 11 && ( + + {columnTotals.valorPago !== 0 ? ( + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valorPago)} + + ) : ( + - + )} - ) : ( - - - )} + )}
-
- {columnTotals.valorConfirmado !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valorConfirmado)} - - ) : ( - - - )} + )) + ))}
-
- {columnTotals.valorPago !== 0 ? ( - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valorPago)} - - ) : ( - - - )} -
-
-
-
-
-
-
- )} +
+ )} {/* Summary Footer - Integrado */} { diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index cab67c1..9eff842 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -176,10 +176,8 @@ export default function Teste() { const meses = [ ...new Set( result.map((item: DREItem) => { - const dataCompetencia = new Date(item.data_competencia); - return `${dataCompetencia.getFullYear()}-${String( - dataCompetencia.getMonth() + 1 - ).padStart(2, "0")}`; + // Usar diretamente o valor de data_competencia que já vem no formato YYYY-MM + return item.data_competencia; }) ), ].sort() as string[]; @@ -489,10 +487,8 @@ export default function Teste() { const valoresPorMes: Record = {}; items.forEach((item) => { - const dataCompetencia = new Date(item.data_competencia); - const anoMes = `${dataCompetencia.getFullYear()}-${String( - dataCompetencia.getMonth() + 1 - ).padStart(2, "0")}`; + // Usar diretamente o valor de data_competencia que já vem no formato YYYY-MM + const anoMes = item.data_competencia; if (!valoresPorMes[anoMes]) { valoresPorMes[anoMes] = 0; @@ -524,11 +520,8 @@ export default function Teste() { // Encontrar o valor do grupo 03 para o mesmo mês const grupo03Items = data.filter((item) => { - const dataCompetencia = new Date(item.data_competencia); - const anoMes = `${dataCompetencia.getFullYear()}-${String( - dataCompetencia.getMonth() + 1 - ).padStart(2, "0")}`; - return anoMes === mes && item.grupo.includes("03"); + // Usar diretamente o valor de data_competencia que já vem no formato YYYY-MM + return item.data_competencia === mes && item.grupo.includes("03"); }); const valorGrupo03 = grupo03Items.reduce( @@ -1201,7 +1194,7 @@ export default function Teste() { {filtrosAplicados && !loading && !error && (
{/* Table Header */} -
+
Descrição
From 362d422fce91a045d2c209c3f03cdabdeb5543b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 10:12:02 -0300 Subject: [PATCH 05/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20da=20expans?= =?UTF-8?q?=C3=A3o=20do=20drill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 100 ++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 9eff842..d6b605c 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -1,6 +1,6 @@ "use client"; -import { LoaderPinwheel, ChevronDown, ChevronRight, Filter } from "lucide-react"; +import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2 } from "lucide-react"; import { useEffect, useState } from "react"; import AnaliticoComponent from "./analitico"; import { Button } from "@/components/ui/button"; @@ -103,6 +103,7 @@ export default function Teste() { linhaSelecionada: "", // Adicionar informação da linha selecionada }); const [linhaSelecionada, setLinhaSelecionada] = useState(null); + const [isAllExpanded, setIsAllExpanded] = useState(false); useEffect(() => { // Carregar períodos disponíveis da API @@ -350,6 +351,29 @@ export default function Teste() { setContasSelecionadas([]); }; + const toggleExpandAll = () => { + if (isAllExpanded) { + // Recolher tudo + setExpandedGroups(new Set()); + setExpandedSubgrupos(new Set()); + setExpandedCentros(new Set()); + setIsAllExpanded(false); + } else { + // Expandir todos os grupos usando dados originais + const todosGrupos = [...new Set(data.map(item => item.grupo))]; + setExpandedGroups(new Set(todosGrupos)); + + // Expandir todos os subgrupos usando dados originais + const todosSubgrupos = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}`))]; + setExpandedSubgrupos(new Set(todosSubgrupos)); + + // Expandir todos os centros de custo usando dados originais (isso também expande as contas automaticamente) + const todosCentros = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}-${item.centro_custo}`))]; + setExpandedCentros(new Set(todosCentros)); + setIsAllExpanded(true); + } + }; + const limparFiltros = () => { const agora = new Date(); const anoAtual = agora.getFullYear(); @@ -377,6 +401,7 @@ export default function Teste() { setDadosFiltrados([]); setFiltrosAplicados(false); setMesesDisponiveis([]); + setIsAllExpanded(false); // Recarregar opções e selecionar todos novamente carregarPeriodosDisponiveis(); @@ -888,8 +913,31 @@ export default function Teste() {
- {/* Botão de Filtro */} - + {/* Controles */} +
+ {/* Botão de Expandir/Recolher */} + + + {/* Botão de Filtro */} +
-
+
{opcoesContas.map(conta => (
@@ -1126,6 +1174,7 @@ export default function Teste() { +
@@ -1208,49 +1257,6 @@ export default function Teste() { ))}
Total
- - {/* Botões de controle */} -
- - -
From 324730c8302378839570e5b81a4e94441c6fe7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 10:23:54 -0300 Subject: [PATCH 06/14] =?UTF-8?q?fix:=20otimiza=C3=A7=C3=A3o=20do=20proces?= =?UTF-8?q?so=20de=20expans=C3=A3o=20do=20drill=20da=20tabela=20sint=C3=A9?= =?UTF-8?q?tica?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 330 +++++++++++++++++++++++++----------------- 1 file changed, 195 insertions(+), 135 deletions(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index d6b605c..51a44ea 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -1,7 +1,7 @@ "use client"; import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2 } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback, startTransition, memo } from "react"; import AnaliticoComponent from "./analitico"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -53,6 +53,123 @@ interface HierarchicalRow { isCalculado?: boolean; } +// Componente memoizado para linhas da tabela +const TableRow = memo(({ + row, + index, + toggleGroup, + toggleSubgrupo, + toggleCentro, + handleRowClick, + getRowStyle, + getIndentStyle, + renderCellContent, + mesesDisponiveis, + formatCurrency, + formatCurrencyWithColor +}: { + row: HierarchicalRow; + index: number; + toggleGroup: (grupo: string) => void; + toggleSubgrupo: (subgrupo: string) => void; + toggleCentro: (centro: string) => void; + handleRowClick: (row: HierarchicalRow, mes?: string) => void; + getRowStyle: (row: HierarchicalRow) => string; + getIndentStyle: (level: number) => React.CSSProperties; + renderCellContent: (row: HierarchicalRow) => React.ReactNode; + mesesDisponiveis: string[]; + formatCurrency: (value: number) => string; + formatCurrencyWithColor: (value: number) => { formatted: string; isNegative: boolean }; +}) => { + return ( +
+
+ {renderCellContent(row)} +
+ + {/* Colunas de valores por mês */} + {mesesDisponiveis.map((mes) => ( +
+
handleRowClick(row, mes)} + title={ + row.valoresPorMes && row.valoresPorMes[mes] + ? formatCurrency(row.valoresPorMes[mes]) + : "-" + } + > + {row.valoresPorMes && row.valoresPorMes[mes] + ? (() => { + const { formatted, isNegative } = + formatCurrencyWithColor(row.valoresPorMes[mes]); + return ( + + {formatted} + + ); + })() + : "-"} +
+
handleRowClick(row, mes)} + title={ + row.percentuaisPorMes && + row.percentuaisPorMes[mes] !== undefined + ? `${row.percentuaisPorMes[mes].toFixed(1)}%` + : "-" + } + > + {row.percentuaisPorMes && + row.percentuaisPorMes[mes] !== undefined + ? `${row.percentuaisPorMes[mes].toFixed(1)}%` + : "-"} +
+
+ ))} + + {/* Coluna Total */} +
handleRowClick(row)} + title={row.total ? formatCurrency(row.total) : "-"} + > + {(() => { + const { formatted, isNegative } = formatCurrencyWithColor( + row.total! + ); + return ( + + {formatted} + + ); + })()} +
+
+ ); +}); + +TableRow.displayName = 'TableRow'; + export default function Teste() { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); @@ -277,35 +394,41 @@ export default function Teste() { setAnaliticoFiltros(novosFiltros); }; - const toggleGroup = (grupo: string) => { - const newExpanded = new Set(expandedGroups); + const toggleGroup = useCallback((grupo: string) => { + setExpandedGroups(prev => { + const newExpanded = new Set(prev); if (newExpanded.has(grupo)) { newExpanded.delete(grupo); } else { newExpanded.add(grupo); } - setExpandedGroups(newExpanded); - }; + return newExpanded; + }); + }, []); - const toggleSubgrupo = (subgrupo: string) => { - const newExpanded = new Set(expandedSubgrupos); + const toggleSubgrupo = useCallback((subgrupo: string) => { + setExpandedSubgrupos(prev => { + const newExpanded = new Set(prev); if (newExpanded.has(subgrupo)) { newExpanded.delete(subgrupo); } else { newExpanded.add(subgrupo); } - setExpandedSubgrupos(newExpanded); - }; + return newExpanded; + }); + }, []); - const toggleCentro = (centro: string) => { - const newExpanded = new Set(expandedCentros); + const toggleCentro = useCallback((centro: string) => { + setExpandedCentros(prev => { + const newExpanded = new Set(prev); if (newExpanded.has(centro)) { newExpanded.delete(centro); } else { newExpanded.add(centro); } - setExpandedCentros(newExpanded); - }; + return newExpanded; + }); + }, []); const handleFiltroChange = (campo: string, valor: string) => { setFiltros(prev => ({ @@ -351,28 +474,29 @@ export default function Teste() { setContasSelecionadas([]); }; - const toggleExpandAll = () => { + const toggleExpandAll = useCallback(() => { if (isAllExpanded) { - // Recolher tudo - setExpandedGroups(new Set()); - setExpandedSubgrupos(new Set()); - setExpandedCentros(new Set()); - setIsAllExpanded(false); + // Recolher tudo - usar startTransition para atualizações não urgentes + startTransition(() => { + setExpandedGroups(new Set()); + setExpandedSubgrupos(new Set()); + setExpandedCentros(new Set()); + setIsAllExpanded(false); + }); } else { - // Expandir todos os grupos usando dados originais - const todosGrupos = [...new Set(data.map(item => item.grupo))]; - setExpandedGroups(new Set(todosGrupos)); - - // Expandir todos os subgrupos usando dados originais - const todosSubgrupos = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}`))]; - setExpandedSubgrupos(new Set(todosSubgrupos)); - - // Expandir todos os centros de custo usando dados originais (isso também expande as contas automaticamente) - const todosCentros = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}-${item.centro_custo}`))]; - setExpandedCentros(new Set(todosCentros)); - setIsAllExpanded(true); + // Expandir todos os grupos usando dados originais - usar startTransition para atualizações não urgentes + startTransition(() => { + const todosGrupos = [...new Set(data.map(item => item.grupo))]; + const todosSubgrupos = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}`))]; + const todosCentros = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}-${item.centro_custo}`))]; + + setExpandedGroups(new Set(todosGrupos)); + setExpandedSubgrupos(new Set(todosSubgrupos)); + setExpandedCentros(new Set(todosCentros)); + setIsAllExpanded(true); + }); } - }; + }, [isAllExpanded, data]); const limparFiltros = () => { const agora = new Date(); @@ -802,12 +926,12 @@ export default function Teste() {
- +
+ {/* Controles */}
{/* Botão de Expandir/Recolher */} - + {/* Botão de Filtro */} @@ -969,7 +1093,7 @@ export default function Teste() { ))} -
+
-
-
+
+
{/* Grupo
@@ -1023,7 +1147,7 @@ export default function Teste() {
-
-
+
{opcoesCentrosCusto.map(centro => (
@@ -1065,7 +1189,7 @@ export default function Teste() { {centrosCustoSelecionados.length} centro(s) selecionado(s)
)} -
+
{/* Conta */}
@@ -1090,7 +1214,7 @@ export default function Teste() { > Limpar -
+
{opcoesContas.map(conta => ( @@ -1252,96 +1376,32 @@ export default function Teste() {
{mes}
% +
-
- ))} + ))}
Total
+
-
{/* Table Body */}
{hierarchicalData.map((row, index) => ( -
-
- {renderCellContent(row)} -
- {mesesDisponiveis.map((mes) => ( -
-
handleRowClick(row, mes)} - title={ - row.valoresPorMes && row.valoresPorMes[mes] - ? formatCurrency(row.valoresPorMes[mes]) - : "-" - } - > - {row.valoresPorMes && row.valoresPorMes[mes] - ? (() => { - const { formatted, isNegative } = - formatCurrencyWithColor(row.valoresPorMes[mes]); - return ( - - {formatted} - - ); - })() - : "-"} -
-
handleRowClick(row, mes)} - title={ - row.percentuaisPorMes && - row.percentuaisPorMes[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-" - } - > - {row.percentuaisPorMes && - row.percentuaisPorMes[mes] !== undefined - ? `${row.percentuaisPorMes[mes].toFixed(1)}%` - : "-"} -
-
- ))} -
handleRowClick(row)} - title={row.total ? formatCurrency(row.total) : "-"} - > - {(() => { - const { formatted, isNegative } = formatCurrencyWithColor( - row.total! - ); - return ( - - {formatted} - - ); - })()} -
-
+ row={row} + index={index} + toggleGroup={toggleGroup} + toggleSubgrupo={toggleSubgrupo} + toggleCentro={toggleCentro} + handleRowClick={handleRowClick} + getRowStyle={getRowStyle} + getIndentStyle={getIndentStyle} + renderCellContent={renderCellContent} + mesesDisponiveis={mesesDisponiveis} + formatCurrency={formatCurrency} + formatCurrencyWithColor={formatCurrencyWithColor} + /> ))}
From acb094271bb867773c551b81388bb6c98cff93f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 10:51:41 -0300 Subject: [PATCH 07/14] =?UTF-8?q?feta:=20inclus=C3=A3o=20da=20funcionalida?= =?UTF-8?q?de=20de=20expandir=20e=20recolher=20os=20dados=20sint=C3=A9tico?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 179 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 157 insertions(+), 22 deletions(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 51a44ea..7f05b55 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -202,6 +202,7 @@ export default function Teste() { const [isFilterOpen, setIsFilterOpen] = useState(false); const [dadosFiltrados, setDadosFiltrados] = useState([]); const [filtrosAplicados, setFiltrosAplicados] = useState(false); + const [ordemHierarquiaContasPrimeiro, setOrdemHierarquiaContasPrimeiro] = useState(false); // Estados para opções dos filtros const [opcoesGrupos, setOpcoesGrupos] = useState([]); @@ -526,6 +527,7 @@ export default function Teste() { setFiltrosAplicados(false); setMesesDisponiveis([]); setIsAllExpanded(false); + setOrdemHierarquiaContasPrimeiro(false); // Recarregar opções e selecionar todos novamente carregarPeriodosDisponiveis(); @@ -786,8 +788,52 @@ export default function Teste() { }); if (expandedSubgrupos.has(`${grupo}-${subgrupo}`)) { - // Agrupar por centro de custo dentro do subgrupo - const centros = subgrupoItems.reduce((acc, item) => { + if (ordemHierarquiaContasPrimeiro) { + // NOVA ORDEM: Contas primeiro, depois Centros de Custo + + // Agrupar por conta dentro do subgrupo + const contas = subgrupoItems.reduce((acc, item) => { + if (!acc[item.conta]) { + acc[item.conta] = []; + } + acc[item.conta].push(item); + return acc; + }, {} as Record); + + // Ordenar contas + const sortedContas = Object.entries(contas).sort(([a], [b]) => + a.localeCompare(b) + ); + + sortedContas.forEach(([conta, contaItems]) => { + const totalConta = contaItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha da conta (Level 2 - antes era Level 3) + const valoresContaPorMes = calcularValoresPorMes(contaItems); + rows.push({ + type: "conta", + level: 2, + grupo, + subgrupo, + conta, + codigo_conta: contaItems[0].codigo_conta, + total: totalConta, + isExpanded: expandedCentros.has( + `${grupo}-${subgrupo}-${conta}` + ), + valoresPorMes: valoresContaPorMes, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresContaPorMes, + grupo + ), + }); + + if (expandedCentros.has(`${grupo}-${subgrupo}-${conta}`)) { + // Agrupar por centro de custo dentro da conta + const centros = contaItems.reduce((acc, item) => { if (!acc[item.centro_custo]) { acc[item.centro_custo] = []; } @@ -796,9 +842,51 @@ export default function Teste() { }, {} as Record); // Ordenar centros de custo - const sortedCentros = Object.entries(centros).sort(([a], [b]) => - a.localeCompare(b) - ); + const sortedCentros = Object.entries(centros).sort(([a], [b]) => + a.localeCompare(b) + ); + + sortedCentros.forEach(([centro, centroItems]) => { + const totalCentro = centroItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha do centro de custo (Level 3 - antes era Level 2) + const valoresCentroPorMes = calcularValoresPorMes(centroItems); + rows.push({ + type: "centro_custo", + level: 3, + grupo, + subgrupo, + centro_custo: centro, + conta, + total: totalCentro, + valoresPorMes: valoresCentroPorMes, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresCentroPorMes, + grupo + ), + }); + }); + } + }); + } else { + // ORDEM ORIGINAL: Centros de Custo primeiro, depois Contas + + // Agrupar por centro de custo dentro do subgrupo + const centros = subgrupoItems.reduce((acc, item) => { + if (!acc[item.centro_custo]) { + acc[item.centro_custo] = []; + } + acc[item.centro_custo].push(item); + return acc; + }, {} as Record); + + // Ordenar centros de custo + const sortedCentros = Object.entries(centros).sort(([a], [b]) => + a.localeCompare(b) + ); sortedCentros.forEach(([centro, centroItems]) => { const totalCentro = centroItems.reduce( @@ -809,7 +897,7 @@ export default function Teste() { // Linha do centro de custo const valoresCentroPorMes = calcularValoresPorMes(centroItems); rows.push({ - type: "centro_custo", + type: "centro_custo", level: 2, grupo, subgrupo, @@ -836,9 +924,9 @@ export default function Teste() { }, {} as Record); // Ordenar contas - const sortedContas = Object.entries(contas).sort(([a], [b]) => - a.localeCompare(b) - ); + const sortedContas = Object.entries(contas).sort(([a], [b]) => + a.localeCompare(b) + ); sortedContas.forEach(([conta, contaItems]) => { const totalConta = contaItems.reduce( @@ -846,10 +934,10 @@ export default function Teste() { 0 ); - // Linha da conta (sem ano/mês no nome) + // Linha da conta const valoresContaPorMes = calcularValoresPorMes(contaItems); rows.push({ - type: "conta", + type: "conta", level: 3, grupo, subgrupo, @@ -866,6 +954,7 @@ export default function Teste() { }); } }); + } } }); } @@ -976,18 +1065,26 @@ export default function Teste() { case "centro_custo": return (
+ {ordemHierarquiaContasPrimeiro ? ( + // Nova ordem: Centro de custo é level 3 (sem botão de toggle) +
+ +
+ ) : ( + // Ordem original: Centro de custo é level 2 (com botão de toggle) + )} + ) : ( + // Ordem original: Conta é level 3 (sem botão de toggle) +
+ +
+ )} -
+
{opcoesContas.map(conta => ( @@ -1283,6 +1397,27 @@ export default function Teste() { placeholder="Pesquise por grupo, subgrupo, centro ou conta" />
*/} + + {/* Ordem da Hierarquia */} +
+ +
+ setOrdemHierarquiaContasPrimeiro(checked)} + /> + +
+
+ {ordemHierarquiaContasPrimeiro + ? "📄 Contas → 🏢 Centros de Custo" + : "🏢 Centros de Custo → 📄 Contas" + } +
+
From 75e452312cd7cfe4c8fae70a3d9d61e8f3b92936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 12:49:04 -0300 Subject: [PATCH 08/14] =?UTF-8?q?fix:=20dupla=20vis=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 416 +++++++++++++++----------------- src/app/api/dre-oracle/route.ts | 3 +- 2 files changed, 191 insertions(+), 228 deletions(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 7f05b55..ceb74be 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -31,6 +31,7 @@ interface DREItem { grupo: string; subgrupo: string; centro_custo: string; + codigo_centro_custo: string; codigo_conta: number; conta: string; valor: string; @@ -44,6 +45,7 @@ interface HierarchicalRow { grupo?: string; subgrupo?: string; centro_custo?: string; + codigo_centro_custo?: string; conta?: string; codigo_conta?: number; total?: number; @@ -58,7 +60,6 @@ const TableRow = memo(({ row, index, toggleGroup, - toggleSubgrupo, toggleCentro, handleRowClick, getRowStyle, @@ -71,7 +72,6 @@ const TableRow = memo(({ row: HierarchicalRow; index: number; toggleGroup: (grupo: string) => void; - toggleSubgrupo: (subgrupo: string) => void; toggleCentro: (centro: string) => void; handleRowClick: (row: HierarchicalRow, mes?: string) => void; getRowStyle: (row: HierarchicalRow) => string; @@ -175,9 +175,6 @@ export default function Teste() { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [expandedGroups, setExpandedGroups] = useState>(new Set()); - const [expandedSubgrupos, setExpandedSubgrupos] = useState>( - new Set() - ); const [expandedCentros, setExpandedCentros] = useState>( new Set() ); @@ -384,7 +381,7 @@ export default function Teste() { const novosFiltros = { dataInicio: dataInicioFiltro, dataFim: dataFimFiltro, - centroCusto: row.centro_custo || "", + centroCusto: row.codigo_centro_custo || "", // ✅ Usar código do centro de custo codigoGrupo, codigoSubgrupo, codigoConta: row.codigo_conta?.toString() || "", @@ -407,17 +404,6 @@ export default function Teste() { }); }, []); - const toggleSubgrupo = useCallback((subgrupo: string) => { - setExpandedSubgrupos(prev => { - const newExpanded = new Set(prev); - if (newExpanded.has(subgrupo)) { - newExpanded.delete(subgrupo); - } else { - newExpanded.add(subgrupo); - } - return newExpanded; - }); - }, []); const toggleCentro = useCallback((centro: string) => { setExpandedCentros(prev => { @@ -443,7 +429,7 @@ export default function Teste() { setCentrosCustoSelecionados(prev => { if (prev.includes(centro)) { return prev.filter(c => c !== centro); - } else { + } else { return [...prev, centro]; } }); @@ -453,7 +439,7 @@ export default function Teste() { setContasSelecionadas(prev => { if (prev.includes(conta)) { return prev.filter(c => c !== conta); - } else { + } else { return [...prev, conta]; } }); @@ -480,24 +466,30 @@ export default function Teste() { // Recolher tudo - usar startTransition para atualizações não urgentes startTransition(() => { setExpandedGroups(new Set()); - setExpandedSubgrupos(new Set()); setExpandedCentros(new Set()); setIsAllExpanded(false); }); } else { - // Expandir todos os grupos usando dados originais - usar startTransition para atualizações não urgentes + // Expandir todos os grupos e contas usando dados originais - usar startTransition para atualizações não urgentes startTransition(() => { const todosGrupos = [...new Set(data.map(item => item.grupo))]; - const todosSubgrupos = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}`))]; - const todosCentros = [...new Set(data.map(item => `${item.grupo}-${item.subgrupo}-${item.centro_custo}`))]; - setExpandedGroups(new Set(todosGrupos)); - setExpandedSubgrupos(new Set(todosSubgrupos)); - setExpandedCentros(new Set(todosCentros)); + if (ordemHierarquiaContasPrimeiro) { + // Nova ordem: expandir grupos e contas + const todasContas = [...new Set(data.map(item => `${item.grupo}-${item.conta}`))]; + setExpandedGroups(new Set(todosGrupos)); + setExpandedCentros(new Set(todasContas)); + } else { + // Ordem original: expandir grupos e centros de custo + const todosCentros = [...new Set(data.map(item => `${item.grupo}-${item.centro_custo}`))]; + setExpandedGroups(new Set(todosGrupos)); + setExpandedCentros(new Set(todosCentros)); + } + setIsAllExpanded(true); }); } - }, [isAllExpanded, data]); + }, [isAllExpanded, data, ordemHierarquiaContasPrimeiro]); const limparFiltros = () => { const agora = new Date(); @@ -741,99 +733,59 @@ export default function Teste() { }); if (expandedGroups.has(grupo)) { - // Agrupar por subgrupo dentro do grupo - const subgrupos = items.reduce((acc, item) => { - // Se o item originalmente era do grupo 05, agrupar tudo sob o nome do grupo 05 - if (item.grupo.includes("05")) { - const subgrupoKey = item.grupo; // Usar o nome completo do grupo 05 - if (!acc[subgrupoKey]) { - acc[subgrupoKey] = []; + if (ordemHierarquiaContasPrimeiro) { + // ORDEM: Grupos → Contas → Centros de Custo + + // Agrupar por conta dentro do grupo + const contas = items.reduce((acc, item) => { + if (!acc[item.conta]) { + acc[item.conta] = []; } - acc[subgrupoKey].push(item); - } else { - // Para outros itens, agrupar normalmente por subgrupo - if (!acc[item.subgrupo]) { - acc[item.subgrupo] = []; - } - acc[item.subgrupo].push(item); - } + acc[item.conta].push(item); return acc; }, {} as Record); - // Ordenar subgrupos - const sortedSubgrupos = Object.entries(subgrupos).sort(([a], [b]) => - a.localeCompare(b) - ); + // Ordenar contas por CODCONTA + const sortedContas = Object.entries(contas).sort(([contaA, itemsA], [contaB, itemsB]) => { + const codcontaA = itemsA[0]?.codigo_conta || 0; + const codcontaB = itemsB[0]?.codigo_conta || 0; + + if (codcontaA && codcontaB) { + return codcontaA - codcontaB; + } + + if (codcontaA && !codcontaB) return -1; + if (!codcontaA && codcontaB) return 1; + + return contaA.localeCompare(contaB); + }); - sortedSubgrupos.forEach(([subgrupo, subgrupoItems]) => { - const totalSubgrupo = subgrupoItems.reduce( + sortedContas.forEach(([conta, contaItems]) => { + const totalConta = contaItems.reduce( (sum, item) => sum + parseFloat(item.valor), 0 ); - // Linha do subgrupo - const valoresSubgrupoPorMes = calcularValoresPorMes(subgrupoItems); + // Linha da conta (Level 1) + const valoresContaPorMes = calcularValoresPorMes(contaItems); rows.push({ - type: "subgrupo", + type: "conta", level: 1, grupo, - subgrupo, - total: totalSubgrupo, - isExpanded: expandedSubgrupos.has(`${grupo}-${subgrupo}`), - valoresPorMes: valoresSubgrupoPorMes, + conta, + codigo_conta: contaItems[0].codigo_conta, + total: totalConta, + isExpanded: expandedCentros.has(`${grupo}-${conta}`), + valoresPorMes: valoresContaPorMes, percentuaisPorMes: calcularPercentuaisPorMes( - valoresSubgrupoPorMes, + valoresContaPorMes, grupo ), }); - if (expandedSubgrupos.has(`${grupo}-${subgrupo}`)) { - if (ordemHierarquiaContasPrimeiro) { - // NOVA ORDEM: Contas primeiro, depois Centros de Custo - - // Agrupar por conta dentro do subgrupo - const contas = subgrupoItems.reduce((acc, item) => { - if (!acc[item.conta]) { - acc[item.conta] = []; - } - acc[item.conta].push(item); - return acc; - }, {} as Record); - - // Ordenar contas - const sortedContas = Object.entries(contas).sort(([a], [b]) => - a.localeCompare(b) - ); - - sortedContas.forEach(([conta, contaItems]) => { - const totalConta = contaItems.reduce( - (sum, item) => sum + parseFloat(item.valor), - 0 - ); - - // Linha da conta (Level 2 - antes era Level 3) - const valoresContaPorMes = calcularValoresPorMes(contaItems); - rows.push({ - type: "conta", - level: 2, - grupo, - subgrupo, - conta, - codigo_conta: contaItems[0].codigo_conta, - total: totalConta, - isExpanded: expandedCentros.has( - `${grupo}-${subgrupo}-${conta}` - ), - valoresPorMes: valoresContaPorMes, - percentuaisPorMes: calcularPercentuaisPorMes( - valoresContaPorMes, - grupo - ), - }); - - if (expandedCentros.has(`${grupo}-${subgrupo}-${conta}`)) { - // Agrupar por centro de custo dentro da conta - const centros = contaItems.reduce((acc, item) => { + if (expandedCentros.has(`${grupo}-${conta}`)) { + // Agrupar por centro de custo dentro da conta + const centros = contaItems.reduce((acc, item) => { if (!acc[item.centro_custo]) { acc[item.centro_custo] = []; } @@ -841,52 +793,72 @@ export default function Teste() { return acc; }, {} as Record); - // Ordenar centros de custo - const sortedCentros = Object.entries(centros).sort(([a], [b]) => - a.localeCompare(b) - ); + // Ordenar centros de custo por CODIGOCENTROCUSTO + const sortedCentros = Object.entries(centros).sort(([centroA, itemsA], [centroB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_centro_custo || ""; + const codigoB = itemsB[0]?.codigo_centro_custo || ""; + + if (codigoA && codigoB) { + return codigoA.localeCompare(codigoB); + } + + if (codigoA && !codigoB) return -1; + if (!codigoA && codigoB) return 1; + + return centroA.localeCompare(centroB); + }); - sortedCentros.forEach(([centro, centroItems]) => { - const totalCentro = centroItems.reduce( + sortedCentros.forEach(([centro, centroItems]) => { + const totalCentro = centroItems.reduce( (sum, item) => sum + parseFloat(item.valor), 0 ); - // Linha do centro de custo (Level 3 - antes era Level 2) - const valoresCentroPorMes = calcularValoresPorMes(centroItems); - rows.push({ - type: "centro_custo", - level: 3, - grupo, - subgrupo, - centro_custo: centro, - conta, - total: totalCentro, - valoresPorMes: valoresCentroPorMes, - percentuaisPorMes: calcularPercentuaisPorMes( - valoresCentroPorMes, - grupo - ), - }); - }); - } + // Linha do centro de custo (Level 2) + const valoresCentroPorMes = calcularValoresPorMes(centroItems); + rows.push({ + type: "centro_custo", + level: 2, + grupo, + centro_custo: centro, + conta, + codigo_centro_custo: centroItems[0].codigo_centro_custo, + total: totalCentro, + valoresPorMes: valoresCentroPorMes, + percentuaisPorMes: calcularPercentuaisPorMes( + valoresCentroPorMes, + grupo + ), + }); }); - } else { - // ORDEM ORIGINAL: Centros de Custo primeiro, depois Contas - - // Agrupar por centro de custo dentro do subgrupo - const centros = subgrupoItems.reduce((acc, item) => { - if (!acc[item.centro_custo]) { - acc[item.centro_custo] = []; - } - acc[item.centro_custo].push(item); - return acc; - }, {} as Record); + } + }); + } else { + // ORDEM ORIGINAL: Grupos → Centros de Custo → Contas + + // Agrupar por centro de custo dentro do grupo + const centros = items.reduce((acc, item) => { + if (!acc[item.centro_custo]) { + acc[item.centro_custo] = []; + } + acc[item.centro_custo].push(item); + return acc; + }, {} as Record); - // Ordenar centros de custo - const sortedCentros = Object.entries(centros).sort(([a], [b]) => - a.localeCompare(b) - ); + // Ordenar centros de custo por CODIGOCENTROCUSTO + const sortedCentros = Object.entries(centros).sort(([centroA, itemsA], [centroB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_centro_custo || ""; + const codigoB = itemsB[0]?.codigo_centro_custo || ""; + + if (codigoA && codigoB) { + return codigoA.localeCompare(codigoB); + } + + if (codigoA && !codigoB) return -1; + if (!codigoA && codigoB) return 1; + + return centroA.localeCompare(centroB); + }); sortedCentros.forEach(([centro, centroItems]) => { const totalCentro = centroItems.reduce( @@ -894,18 +866,16 @@ export default function Teste() { 0 ); - // Linha do centro de custo + // Linha do centro de custo (Level 1) const valoresCentroPorMes = calcularValoresPorMes(centroItems); rows.push({ - type: "centro_custo", - level: 2, + type: "centro_custo", + level: 1, grupo, - subgrupo, centro_custo: centro, + codigo_centro_custo: centroItems[0].codigo_centro_custo, total: totalCentro, - isExpanded: expandedCentros.has( - `${grupo}-${subgrupo}-${centro}` - ), + isExpanded: expandedCentros.has(`${grupo}-${centro}`), valoresPorMes: valoresCentroPorMes, percentuaisPorMes: calcularPercentuaisPorMes( valoresCentroPorMes, @@ -913,7 +883,7 @@ export default function Teste() { ), }); - if (expandedCentros.has(`${grupo}-${subgrupo}-${centro}`)) { + if (expandedCentros.has(`${grupo}-${centro}`)) { // Agrupar por conta dentro do centro de custo const contas = centroItems.reduce((acc, item) => { if (!acc[item.conta]) { @@ -923,10 +893,20 @@ export default function Teste() { return acc; }, {} as Record); - // Ordenar contas - const sortedContas = Object.entries(contas).sort(([a], [b]) => - a.localeCompare(b) - ); + // Ordenar contas por CODCONTA + const sortedContas = Object.entries(contas).sort(([contaA, itemsA], [contaB, itemsB]) => { + const codcontaA = itemsA[0]?.codigo_conta || 0; + const codcontaB = itemsB[0]?.codigo_conta || 0; + + if (codcontaA && codcontaB) { + return codcontaA - codcontaB; + } + + if (codcontaA && !codcontaB) return -1; + if (!codcontaA && codcontaB) return 1; + + return contaA.localeCompare(contaB); + }); sortedContas.forEach(([conta, contaItems]) => { const totalConta = contaItems.reduce( @@ -934,13 +914,12 @@ export default function Teste() { 0 ); - // Linha da conta + // Linha da conta (Level 2) const valoresContaPorMes = calcularValoresPorMes(contaItems); rows.push({ - type: "conta", - level: 3, + type: "conta", + level: 2, grupo, - subgrupo, centro_custo: centro, conta, codigo_conta: contaItems[0].codigo_conta, @@ -954,9 +933,7 @@ export default function Teste() { }); } }); - } } - }); } }); @@ -1032,78 +1009,19 @@ export default function Teste() { {row.grupo} {isCalculado && ( - // - <> + )} ); - case "subgrupo": - return ( -
- - -
- ); - case "centro_custo": - return ( -
- {ordemHierarquiaContasPrimeiro ? ( - // Nova ordem: Centro de custo é level 3 (sem botão de toggle) -
- -
- ) : ( - // Ordem original: Centro de custo é level 2 (com botão de toggle) - - )} - -
- ); case "conta": return (
{ordemHierarquiaContasPrimeiro ? ( - // Nova ordem: Conta é level 2 (com botão de toggle) - + ) : ( - // Ordem original: Conta é level 3 (sem botão de toggle) + // Ordem original: Conta é level 2 (sem botão de toggle)
@@ -1122,7 +1040,52 @@ export default function Teste() { onClick={() => handleRowClick(row)} className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate" > - {row.conta} +
+ + {row.conta} + + {row.codigo_conta && ( + + {row.codigo_conta} + + )} +
+ +
+ ); + case "centro_custo": + return ( +
+ {ordemHierarquiaContasPrimeiro ? ( + // Nova ordem: Centro de custo é level 2 (sem botão de toggle) +
+ +
+ ) : ( + // Ordem original: Centro de custo é level 1 (com botão de toggle) + + )} +
); @@ -1527,7 +1490,6 @@ export default function Teste() { row={row} index={index} toggleGroup={toggleGroup} - toggleSubgrupo={toggleSubgrupo} toggleCentro={toggleCentro} handleRowClick={handleRowClick} getRowStyle={getRowStyle} diff --git a/src/app/api/dre-oracle/route.ts b/src/app/api/dre-oracle/route.ts index 463dd89..4509281 100644 --- a/src/app/api/dre-oracle/route.ts +++ b/src/app/api/dre-oracle/route.ts @@ -23,7 +23,8 @@ export async function GET(request: NextRequest) { data_cai: item.DATA || "2023-03", // Usar DATA do Oracle grupo: codgrupo ? `${codgrupo} - ${nomeGrupo}` : nomeGrupo, // Concatenar código + nome subgrupo: item.CENTROCUSTO || "", // Usar CENTROCUSTO como subgrupo - centro_custo: item.CODIGOCENTROCUSTO || "", // Usar CODIGOCENTROCUSTO + centro_custo: item.CENTROCUSTO || "", // Usar CENTROCUSTO (nome do centro) + codigo_centro_custo: item.CODIGOCENTROCUSTO || "", // Usar CODIGOCENTROCUSTO (código do centro) codigo_conta: parseInt(item.CODCONTA) || 0, // Converter CODCONTA para número conta: item.CONTA || "", // Usar CONTA do Oracle valor: item.VALOR?.toString() || "0", // Converter VALOR para string From be89e103c5ddcd4855a49c4e7a53f77af22f33de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 13:55:47 -0300 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20dos=20paramet?= =?UTF-8?q?ro=20do=20filtro=20analitico?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/analitico.tsx | 15 ++++++++++++++ src/app/DRE/teste.tsx | 42 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/app/DRE/analitico.tsx b/src/app/DRE/analitico.tsx index 716c3c1..28595ad 100644 --- a/src/app/DRE/analitico.tsx +++ b/src/app/DRE/analitico.tsx @@ -91,6 +91,14 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { console.log('🔄 Analítico - useEffect dos filtros chamado'); console.log('📋 Filtros recebidos via props:', filtros); console.log('📋 Filtros externos atuais:', filtrosExternos); + console.log('🔍 Detalhes dos filtros:', { + dataInicio: filtros.dataInicio, + dataFim: filtros.dataFim, + centroCusto: filtros.centroCusto, + codigoGrupo: filtros.codigoGrupo, + codigoConta: filtros.codigoConta, + linhaSelecionada: filtros.linhaSelecionada + }); setFiltrosExternos(filtros); }, [filtros, filtrosExternos]); @@ -128,6 +136,13 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { const url = `/api/analitico-oracle?${params.toString()}`; console.log('🌐 Fazendo requisição para:', url); + console.log('📋 Parâmetros enviados:', { + dataInicio: filtrosExternos.dataInicio, + dataFim: filtrosExternos.dataFim, + centroCusto: filtrosExternos.centroCusto, + codigoGrupo: filtrosExternos.codigoGrupo, + codigoConta: filtrosExternos.codigoConta + }); const response = await fetch(url); if (response.ok) { diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index ceb74be..bebffb2 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -378,17 +378,53 @@ export default function Teste() { const dataInicioFiltro = mesSelecionado || dataInicioStr; const dataFimFiltro = mesSelecionado || dataFimStr; + // Determinar filtros baseado na ordem hierárquica e tipo da linha + let centroCustoFiltro = ""; + let codigoContaFiltro = ""; + + if (ordemHierarquiaContasPrimeiro) { + // Ordem: Contas → Centros de Custo + // SEMPRE filtrar por código da conta, independente do tipo da linha + codigoContaFiltro = row.codigo_conta?.toString() || ""; + // Se for centro de custo, também filtrar por código do centro de custo + centroCustoFiltro = row.type === "centro_custo" ? (row.codigo_centro_custo || "") : ""; + } else { + // Ordem: Centros de Custo → Contas + // SEMPRE filtrar por código do centro de custo, independente do tipo da linha + centroCustoFiltro = row.codigo_centro_custo || ""; + codigoContaFiltro = ""; // Nunca filtrar por conta nesta ordem + } + + console.log('🎯 Filtros determinados:', { + centroCustoFiltro, + codigoContaFiltro, + ordemHierarquia: ordemHierarquiaContasPrimeiro ? 'Contas→Centros' : 'Centros→Contas', + tipoLinha: row.type, + rowData: { + codigo_conta: row.codigo_conta, + codigo_centro_custo: row.codigo_centro_custo, + centro_custo: row.centro_custo, + conta: row.conta + } + }); + const novosFiltros = { dataInicio: dataInicioFiltro, dataFim: dataFimFiltro, - centroCusto: row.codigo_centro_custo || "", // ✅ Usar código do centro de custo + centroCusto: centroCustoFiltro, codigoGrupo, codigoSubgrupo, - codigoConta: row.codigo_conta?.toString() || "", + codigoConta: codigoContaFiltro, linhaSelecionada: row.grupo || row.subgrupo || row.centro_custo || row.conta || "", // Incluir informação da linha selecionada }; console.log('🎯 Novos filtros para analítico:', novosFiltros); + console.log('🔍 Verificação final dos filtros:', { + centroCusto: novosFiltros.centroCusto, + codigoConta: novosFiltros.codigoConta, + codigoContaType: typeof novosFiltros.codigoConta, + codigoContaLength: novosFiltros.codigoConta?.length + }); setAnaliticoFiltros(novosFiltros); }; @@ -822,6 +858,7 @@ export default function Teste() { grupo, centro_custo: centro, conta, + codigo_conta: contaItems[0].codigo_conta, // ✅ Adicionar código da conta codigo_centro_custo: centroItems[0].codigo_centro_custo, total: totalCentro, valoresPorMes: valoresCentroPorMes, @@ -873,6 +910,7 @@ export default function Teste() { level: 1, grupo, centro_custo: centro, + codigo_conta: centroItems[0].codigo_conta, // ✅ Adicionar código da conta codigo_centro_custo: centroItems[0].codigo_centro_custo, total: totalCentro, isExpanded: expandedCentros.has(`${grupo}-${centro}`), From beeedbff69d9094c0abe0be8233eee7de9e11738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 14:09:48 -0300 Subject: [PATCH 10/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20do=20filtro?= =?UTF-8?q?=20do=20drill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index bebffb2..6e72523 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -392,7 +392,8 @@ export default function Teste() { // Ordem: Centros de Custo → Contas // SEMPRE filtrar por código do centro de custo, independente do tipo da linha centroCustoFiltro = row.codigo_centro_custo || ""; - codigoContaFiltro = ""; // Nunca filtrar por conta nesta ordem + // Se for conta, também filtrar por código da conta + codigoContaFiltro = row.type === "conta" ? (row.codigo_conta?.toString() || "") : ""; } console.log('🎯 Filtros determinados:', { @@ -810,6 +811,7 @@ export default function Teste() { grupo, conta, codigo_conta: contaItems[0].codigo_conta, + codigo_centro_custo: contaItems[0].codigo_centro_custo, // ✅ Adicionar código do centro de custo total: totalConta, isExpanded: expandedCentros.has(`${grupo}-${conta}`), valoresPorMes: valoresContaPorMes, @@ -961,6 +963,7 @@ export default function Teste() { centro_custo: centro, conta, codigo_conta: contaItems[0].codigo_conta, + codigo_centro_custo: centroItems[0].codigo_centro_custo, // ✅ Adicionar código do centro de custo total: totalConta, valoresPorMes: valoresContaPorMes, percentuaisPorMes: calcularPercentuaisPorMes( From a5ce93042c1b36d694b8354030694545c64917dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 14:19:16 -0300 Subject: [PATCH 11/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20da=20exporta?= =?UTF-8?q?=C3=A7=C3=A3o=20dos=20dados=20sint=C3=A9ticos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 308 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 307 insertions(+), 1 deletion(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 6e72523..f04a8eb 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -1,12 +1,13 @@ "use client"; -import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2 } from "lucide-react"; +import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2, Download } from "lucide-react"; import { useEffect, useState, useCallback, startTransition, memo } from "react"; import AnaliticoComponent from "./analitico"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; +import * as XLSX from "xlsx"; import { Sheet, SheetContent, @@ -498,6 +499,299 @@ export default function Teste() { setContasSelecionadas([]); }; + const exportarXLSX = () => { + if (!data.length) { + console.log('⚠️ Nenhum dado para exportar'); + return; + } + + console.log('📊 Exportando TODOS os dados expandidos para XLSX...'); + + // Criar uma versão completamente expandida dos dados hierárquicos + const dadosCompletosExpandidos = buildHierarchicalDataCompleta(); + + // Preparar dados para exportação + const dadosExportacao = dadosCompletosExpandidos.map((row, index) => { + const linha: any = { + 'Linha': index + 1, + 'Tipo': row.type, + 'Nível': row.level, + 'Grupo': row.grupo || '', + 'Centro de Custo': row.centro_custo || '', + 'Conta': row.conta || '', + 'Código Centro': row.codigo_centro_custo || '', + 'Código Conta': row.codigo_conta || '', + 'Total': row.total || 0, + }; + + // Adicionar colunas dos meses + mesesDisponiveis.forEach(mes => { + const valor = row.valoresPorMes?.[mes] || 0; + const percentual = row.percentuaisPorMes?.[mes] || 0; + linha[`Valor ${mes}`] = valor; + linha[`% ${mes}`] = percentual; + }); + + return linha; + }); + + // Criar workbook + const wb = XLSX.utils.book_new(); + + // Criar worksheet principal + const ws = XLSX.utils.json_to_sheet(dadosExportacao); + + // Ajustar largura das colunas + const colWidths = [ + { wch: 8 }, // Linha + { wch: 15 }, // Tipo + { wch: 8 }, // Nível + { wch: 30 }, // Grupo + { wch: 25 }, // Centro de Custo + { wch: 35 }, // Conta + { wch: 15 }, // Código Centro + { wch: 12 }, // Código Conta + { wch: 15 }, // Total + ]; + + // Adicionar larguras para colunas dos meses + mesesDisponiveis.forEach(() => { + colWidths.push({ wch: 15 }); // Valor + colWidths.push({ wch: 10 }); // % + }); + + ws['!cols'] = colWidths; + + // Adicionar worksheet ao workbook + XLSX.utils.book_append_sheet(wb, ws, 'DRE Gerencial Completo'); + + // Criar worksheet de resumo + const resumoData = [ + { 'Informação': 'Período', 'Valor': `${filtros.periodoDe} a ${filtros.periodoAte}` }, + { 'Informação': 'Grupo', 'Valor': filtros.grupo }, + { 'Informação': 'Subgrupo', 'Valor': filtros.subgrupo }, + { 'Informação': 'Centro de Custo', 'Valor': filtros.centroCusto }, + { 'Informação': 'Conta', 'Valor': filtros.conta }, + { 'Informação': 'Valor Mínimo', 'Valor': filtros.valorMin || 'N/A' }, + { 'Informação': 'Valor Máximo', 'Valor': filtros.valorMax || 'N/A' }, + { 'Informação': 'Busca Textual', 'Valor': filtros.buscaTextual || 'N/A' }, + { 'Informação': 'Ordem Hierárquica', 'Valor': ordemHierarquiaContasPrimeiro ? 'Contas → Centros' : 'Centros → Contas' }, + { 'Informação': 'Total de Registros', 'Valor': dadosCompletosExpandidos.length }, + { 'Informação': 'Data de Exportação', 'Valor': new Date().toLocaleString('pt-BR') }, + ]; + + const wsResumo = XLSX.utils.json_to_sheet(resumoData); + wsResumo['!cols'] = [{ wch: 20 }, { wch: 30 }]; + XLSX.utils.book_append_sheet(wb, wsResumo, 'Resumo'); + + // Gerar nome do arquivo + const dataAtual = new Date().toISOString().split('T')[0]; + const nomeArquivo = `DRE_Gerencial_Completo_${dataAtual}.xlsx`; + + // Exportar arquivo + XLSX.writeFile(wb, nomeArquivo); + + console.log('✅ Arquivo XLSX completo exportado:', nomeArquivo); + }; + + // Função para construir dados hierárquicos completamente expandidos + const buildHierarchicalDataCompleta = (): HierarchicalRow[] => { + const rows: HierarchicalRow[] = []; + + // Agrupar dados por grupo + const grupos = data.reduce((acc, item) => { + if (!acc[item.grupo]) { + acc[item.grupo] = []; + } + acc[item.grupo].push(item); + return acc; + }, {} as Record); + + // Ordenar grupos por código + const sortedGrupos = Object.entries(grupos).sort(([grupoA, itemsA], [grupoB, itemsB]) => { + const codigoA = itemsA[0]?.codgrupo || ""; + const codigoB = itemsB[0]?.codgrupo || ""; + return codigoA.localeCompare(codigoB); + }); + + sortedGrupos.forEach(([grupo, items]) => { + const totalGrupo = items.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha do grupo (Level 0) + const valoresGrupoPorMes = calcularValoresPorMes(items); + rows.push({ + type: "grupo", + level: 0, + grupo, + total: totalGrupo, + valoresPorMes: valoresGrupoPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresGrupoPorMes, grupo), + isCalculado: items[0]?.isCalculado || false, + }); + + if (ordemHierarquiaContasPrimeiro) { + // ORDEM: Grupos → Contas → Centros de Custo + + // Agrupar por conta dentro do grupo + const contas = items.reduce((acc, item) => { + if (!acc[item.conta]) { + acc[item.conta] = []; + } + acc[item.conta].push(item); + return acc; + }, {} as Record); + + // Ordenar contas por CODCONTA + const sortedContas = Object.entries(contas).sort(([contaA, itemsA], [contaB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_conta || 0; + const codigoB = itemsB[0]?.codigo_conta || 0; + return codigoA - codigoB; + }); + + sortedContas.forEach(([conta, contaItems]) => { + const totalConta = contaItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha da conta (Level 1) + const valoresContaPorMes = calcularValoresPorMes(contaItems); + rows.push({ + type: "conta", + level: 1, + grupo, + conta, + codigo_conta: contaItems[0].codigo_conta, + codigo_centro_custo: contaItems[0].codigo_centro_custo, + total: totalConta, + valoresPorMes: valoresContaPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes, grupo), + }); + + // Agrupar por centro de custo dentro da conta + const centros = contaItems.reduce((acc, item) => { + if (!acc[item.centro_custo]) { + acc[item.centro_custo] = []; + } + acc[item.centro_custo].push(item); + return acc; + }, {} as Record); + + // Ordenar centros de custo por CODIGOCENTROCUSTO + const sortedCentros = Object.entries(centros).sort(([centroA, itemsA], [centroB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_centro_custo || ""; + const codigoB = itemsB[0]?.codigo_centro_custo || ""; + return codigoA.localeCompare(codigoB); + }); + + sortedCentros.forEach(([centro, centroItems]) => { + const totalCentro = centroItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha do centro de custo (Level 2) + const valoresCentroPorMes = calcularValoresPorMes(centroItems); + rows.push({ + type: "centro_custo", + level: 2, + grupo, + centro_custo: centro, + conta, + codigo_conta: contaItems[0].codigo_conta, + codigo_centro_custo: centroItems[0].codigo_centro_custo, + total: totalCentro, + valoresPorMes: valoresCentroPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresCentroPorMes, grupo), + }); + }); + }); + } else { + // ORDEM ORIGINAL: Grupos → Centros de Custo → Contas + + // Agrupar por centro de custo dentro do grupo + const centros = items.reduce((acc, item) => { + if (!acc[item.centro_custo]) { + acc[item.centro_custo] = []; + } + acc[item.centro_custo].push(item); + return acc; + }, {} as Record); + + // Ordenar centros de custo por CODIGOCENTROCUSTO + const sortedCentros = Object.entries(centros).sort(([centroA, itemsA], [centroB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_centro_custo || ""; + const codigoB = itemsB[0]?.codigo_centro_custo || ""; + return codigoA.localeCompare(codigoB); + }); + + sortedCentros.forEach(([centro, centroItems]) => { + const totalCentro = centroItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha do centro de custo (Level 1) + const valoresCentroPorMes = calcularValoresPorMes(centroItems); + rows.push({ + type: "centro_custo", + level: 1, + grupo, + centro_custo: centro, + codigo_conta: centroItems[0].codigo_conta, + codigo_centro_custo: centroItems[0].codigo_centro_custo, + total: totalCentro, + valoresPorMes: valoresCentroPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresCentroPorMes, grupo), + }); + + // Agrupar por conta dentro do centro de custo + const contas = centroItems.reduce((acc, item) => { + if (!acc[item.conta]) { + acc[item.conta] = []; + } + acc[item.conta].push(item); + return acc; + }, {} as Record); + + // Ordenar contas por CODCONTA + const sortedContas = Object.entries(contas).sort(([contaA, itemsA], [contaB, itemsB]) => { + const codigoA = itemsA[0]?.codigo_conta || 0; + const codigoB = itemsB[0]?.codigo_conta || 0; + return codigoA - codigoB; + }); + + sortedContas.forEach(([conta, contaItems]) => { + const totalConta = contaItems.reduce( + (sum, item) => sum + parseFloat(item.valor), + 0 + ); + + // Linha da conta (Level 2) + const valoresContaPorMes = calcularValoresPorMes(contaItems); + rows.push({ + type: "conta", + level: 2, + grupo, + centro_custo: centro, + conta, + codigo_conta: contaItems[0].codigo_conta, + codigo_centro_custo: centroItems[0].codigo_centro_custo, + total: totalConta, + valoresPorMes: valoresContaPorMes, + percentuaisPorMes: calcularPercentuaisPorMes(valoresContaPorMes, grupo), + }); + }); + }); + } + }); + + return rows; + }; + const toggleExpandAll = useCallback(() => { if (isAllExpanded) { // Recolher tudo - usar startTransition para atualizações não urgentes @@ -1157,6 +1451,18 @@ export default function Teste() { {/* Controles */}
+ {/* Botão de Exportar XLSX */} + + {/* Botão de Expandir/Recolher */}
From e64a87c166b1b264963c0a102ed975735cce9cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 14:54:58 -0300 Subject: [PATCH 13/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20do=20filtro?= =?UTF-8?q?=20de=20centro=20custo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 291c521..02c310a 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -1,7 +1,7 @@ "use client"; import { LoaderPinwheel, ChevronDown, ChevronRight, Filter, Maximize2, Minimize2, Download } from "lucide-react"; -import { useEffect, useState, useCallback, startTransition, memo } from "react"; +import React, { useEffect, useState, useCallback, startTransition, memo } from "react"; import AnaliticoComponent from "./analitico"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -196,6 +196,8 @@ export default function Teste() { // Estados para multi-seleção const [centrosCustoSelecionados, setCentrosCustoSelecionados] = useState([]); + // Estado para armazenar os códigos dos centros de custo + const [codigosCentrosCusto, setCodigosCentrosCusto] = useState>({}); const [contasSelecionadas, setContasSelecionadas] = useState([]); const [isFilterOpen, setIsFilterOpen] = useState(false); const [dadosFiltrados, setDadosFiltrados] = useState([]); @@ -259,9 +261,25 @@ export default function Teste() { const subgruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.subgrupo))].sort() as string[]; setOpcoesSubgrupos(subgruposUnicos); - // Extrair centros de custo únicos + // Extrair centros de custo únicos com nome e código const centrosCustoUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.centro_custo))].sort() as string[]; - setOpcoesCentrosCusto(centrosCustoUnicos); + + // Filtrar centro de custo com código específico + const centrosCustoFiltrados = centrosCustoUnicos.filter(centro => { + const item = dadosCompletos.find((d: DREItem) => d.centro_custo === centro); + return item?.codigo_centro_custo !== "002.003.017"; + }); + + setOpcoesCentrosCusto(centrosCustoFiltrados); + + // Criar objeto de códigos dos centros de custo + const codigos: Record = {}; + dadosCompletos.forEach((item: DREItem) => { + if (item.centro_custo && item.codigo_centro_custo) { + codigos[item.centro_custo] = item.codigo_centro_custo; + } + }); + setCodigosCentrosCusto(codigos); // Extrair contas únicas const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; @@ -499,6 +517,22 @@ export default function Teste() { setContasSelecionadas([]); }; + // Função auxiliar para obter o código do centro de custo + const obterCodigoCentroCusto = React.useCallback((nomeCentro: string): string => { + if (!data || data.length === 0) { + return ''; + } + + // Buscar o primeiro item que corresponde ao nome do centro de custo + const item = data.find(item => item.centro_custo === nomeCentro); + + if (item && item.codigo_centro_custo) { + return item.codigo_centro_custo; + } + + return ''; + }, [data]); + const exportarXLSX = () => { if (!data.length) { console.log('⚠️ Nenhum dado para exportar'); @@ -1603,7 +1637,7 @@ export default function Teste() { htmlFor={`centro-${centro}`} className="text-sm font-normal cursor-pointer flex-1" > - {centro} + {centro}{codigosCentrosCusto[centro] ? ` - ${codigosCentrosCusto[centro]}` : ''} ))} From 7e2f66bdbae43eb27a08d0e96d831d662839a3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Wed, 22 Oct 2025 14:59:37 -0300 Subject: [PATCH 14/14] =?UTF-8?q?fix:=20corre=C3=A7=C3=A3o=20do=20filtro,?= =?UTF-8?q?=20lista=20de=20contas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/DRE/teste.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 02c310a..a0bfa35 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -199,6 +199,8 @@ export default function Teste() { // Estado para armazenar os códigos dos centros de custo const [codigosCentrosCusto, setCodigosCentrosCusto] = useState>({}); const [contasSelecionadas, setContasSelecionadas] = useState([]); + // Estado para armazenar os códigos das contas + const [codigosContas, setCodigosContas] = useState>({}); const [isFilterOpen, setIsFilterOpen] = useState(false); const [dadosFiltrados, setDadosFiltrados] = useState([]); const [filtrosAplicados, setFiltrosAplicados] = useState(false); @@ -285,6 +287,15 @@ export default function Teste() { const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; setOpcoesContas(contasUnicas); + // Criar objeto de códigos das contas + const codigosContasObj: Record = {}; + dadosCompletos.forEach((item: DREItem) => { + if (item.conta && item.codigo_conta) { + codigosContasObj[item.conta] = item.codigo_conta.toString(); + } + }); + setCodigosContas(codigosContasObj); + // Inicializar com todos os itens selecionados setCentrosCustoSelecionados(centrosCustoUnicos); setContasSelecionadas(contasUnicas); @@ -1686,7 +1697,7 @@ export default function Teste() { htmlFor={`conta-${conta}`} className="text-sm font-normal cursor-pointer flex-1" > - {conta} + {conta}{codigosContas[conta] ? ` - ${codigosContas[conta]}` : ''} ))}