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/analitico.tsx b/src/app/DRE/analitico.tsx index ef6d98f..28595ad 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"; @@ -67,6 +68,7 @@ interface AnaliticoProps { codigoGrupo?: string; codigoSubgrupo?: string; codigoConta?: string; + linhaSelecionada?: string; // Adicionar propriedade para linha selecionada }; } @@ -75,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: "" }, @@ -88,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]); @@ -125,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) { @@ -153,6 +171,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"); @@ -162,6 +183,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"); @@ -171,6 +195,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 || "-"; @@ -180,31 +207,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", { @@ -223,6 +268,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 "-"; @@ -242,6 +290,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 "-"; @@ -261,6 +312,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 "-"; @@ -280,16 +334,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 || "-"; @@ -341,13 +404,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); @@ -542,39 +608,70 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { }; return ( -
+
{/* Header Section */} -
-
- {/*
- - - -
*/} +
+

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

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:
@@ -582,72 +679,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 */} @@ -656,38 +708,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {/* Table Header */}
-
- 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 -
+ {table.getHeaderGroups().map(headerGroup => ( + headerGroup.headers.map(header => ( +
+ {header.isPlaceholder ? null : ( +
+ {String(header.column.columnDef.header)} + {header.column.getCanResize() && ( +
+ )} +
+ )} +
+ )) + ))}
@@ -700,13 +742,13 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { scrollbarColor: "#cbd5e0 #f7fafc", }} > - {loading ? ( + {loading ? (

Carregando dados...

-
-
+
+
) : virtualRows.length === 0 ? (
@@ -724,11 +766,11 @@ 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")} -
-
- {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 || "-"} + {row.getVisibleCells().map(cell => ( +
+ {String(cell.renderValue() || '')}
+ ))}
); })}
- )} + )} {/* Footer com Totalizador das Colunas */} {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)} - - ) : ( - - - )} -
-
-
-
-
+ )) + ))}
)} @@ -987,7 +941,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { {conditions.map((cond, idx) => (
- +
); - case "subgrupo": - return ( -
- - -
- ); - case "centro_custo": - return ( -
- - -
- ); case "conta": return ( -
-
- -
+
+ {ordemHierarquiaContasPrimeiro ? ( + // Nova ordem: Conta é level 1 (com botão de toggle) + + ) : ( + // Ordem original: Conta é level 2 (sem botão de toggle) +
+ +
+ )} +
+ ); + 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) + + )} +
); @@ -819,21 +1481,56 @@ export default function Teste() { const hierarchicalData = buildHierarchicalData(); return ( -
+
{/* Header Section */} -
-
-
+
+
+

DRE Gerencial

Demonstração do Resultado do Exercício

-
- - {/* Botão de Filtro */} - +
+ + {/* Controles */} +
+ {/* Botão de Exportar XLSX */} + + + {/* Botão de Expandir/Recolher */} + + + {/* Botão de Filtro */} + + +
+
+
+ {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 -
+
-
+
@@ -980,7 +1743,7 @@ export default function Teste() {
*/} {/* Busca Textual -
+
*/} + + {/* Ordem da Hierarquia */} +
+ +
+ setOrdemHierarquiaContasPrimeiro(checked)} + /> + +
+
+ {ordemHierarquiaContasPrimeiro + ? "📄 Contas → 🏢 Centros de Custo" + : "🏢 Centros de Custo → 📄 Contas" + } +
+
- + {/* */} @@ -1004,18 +1788,19 @@ export default function Teste() { +
{/* Loading quando aplicando filtros */} {loading && ( -
-
-
+
+
+
-

+

Aplicando filtros...

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

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

{error}

-
@@ -1050,16 +1835,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.

@@ -1072,103 +1857,40 @@ export default function Teste() { {filtrosAplicados && !loading && !error && (
{/* Table Header */} -
-
-
Descrição
- {mesesDisponiveis.map((mes) => ( -
-
{mes}
-
+
+
+
+
Descrição
+ {mesesDisponiveis.map((mes) => ( +
+
{mes}
+
% +
+ ))} +
Total
- ))} -
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} + toggleCentro={toggleCentro} + handleRowClick={handleRowClick} + getRowStyle={getRowStyle} + getIndentStyle={getIndentStyle} + renderCellContent={renderCellContent} + mesesDisponiveis={mesesDisponiveis} + formatCurrency={formatCurrency} + formatCurrencyWithColor={formatCurrencyWithColor} + /> ))}
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 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 }