fix: correção da ordem dos valores das colunas por periodo na tabela sintética

This commit is contained in:
Alessandro Gonçaalves 2025-10-22 09:53:57 -03:00
parent 1375a471a2
commit 4226fa6c76
2 changed files with 168 additions and 223 deletions

View File

@ -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<ColumnFiltersState>([]);
const [columnSizing, setColumnSizing] = React.useState<ColumnSizingState>({});
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<HTMLDivElement>(null);
@ -643,38 +693,28 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{/* Table Header */}
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200 sticky top-0 z-20">
<div className="flex items-center px-4 py-3 text-xs font-semibold text-gray-700 uppercase tracking-wide">
<div className="w-[140px] whitespace-nowrap">
Data de Vencimento
{table.getHeaderGroups().map(headerGroup => (
headerGroup.headers.map(header => (
<div
key={header.id}
className="whitespace-nowrap flex items-center"
style={{ width: header.getSize() }}
>
{header.isPlaceholder ? null : (
<div className="flex items-center gap-2 w-full">
<span>{String(header.column.columnDef.header)}</span>
{header.column.getCanResize() && (
<div
className="w-1 h-6 bg-gray-300 hover:bg-blue-500 cursor-col-resize"
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
/>
)}
</div>
<div className="w-[120px] whitespace-nowrap">Data de Caixa</div>
<div className="w-[120px] whitespace-nowrap">Entidade</div>
<div className="w-[120px] whitespace-nowrap">
Cód. Fornec
</div>
<div className="w-[220px] whitespace-nowrap">
Nome do Fornecedor
</div>
<div className="w-[140px] whitespace-nowrap">Centro de Custo</div>
<div className="w-[130px] whitespace-nowrap">Código da Conta</div>
<div className="w-[160px] whitespace-nowrap">Nome da Conta</div>
<div className="w-[130px] whitespace-nowrap text-right">
Valor Realizado
</div>
<div className="w-[120px] whitespace-nowrap text-right">
Valor Previsto
</div>
<div className="w-[130px] whitespace-nowrap text-right">
Valor Confirmado
</div>
<div className="w-[140px] whitespace-nowrap text-right">
Valor Pago
</div>
<div className="w-[20px] whitespace-nowrap"></div>
<div className="w-[320px] whitespace-nowrap">Histórico</div>
<div className="w-[500px] whitespace-nowrap">Histórico 2</div>
<div className="w-[30px] whitespace-nowrap">
Número do Lançamento
)}
</div>
))
))}
</div>
</div>
@ -728,110 +768,15 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
className="absolute top-0 left-0 w-full flex items-center px-4 py-3 text-sm border-b border-gray-100 hover:bg-gray-50 transition-colors"
style={{ transform: `translateY(${virtualRow.start}px)` }}
>
<div className="w-[140px] text-gray-600 whitespace-nowrap">
{new Date(
row.original.data_vencimento
).toLocaleDateString("pt-BR")}
</div>
<div className="w-[120px] text-gray-600 whitespace-nowrap">
{new Date(row.original.data_caixa).toLocaleDateString(
"pt-BR"
)}
</div>
<div className="w-[120px] text-gray-500 whitespace-nowrap">
{row.original.entidade || "-"}
</div>
<div className="w-[120px] font-medium text-gray-900 whitespace-nowrap">
{row.original.codigo_fornecedor}
</div>
{row.getVisibleCells().map(cell => (
<div
className="w-[220px] text-gray-700 truncate"
title={row.original.nome_fornecedor}
key={cell.id}
className="whitespace-nowrap overflow-hidden"
style={{ width: cell.column.getSize() }}
>
{row.original.nome_fornecedor}
</div>
<div className="w-[140px] text-gray-600 whitespace-nowrap">
{row.original.codigo_centrocusto}
</div>
<div className="w-[130px] text-gray-600 whitespace-nowrap">
{row.original.codigo_conta}
</div>
<div
className="w-[160px] text-gray-700 truncate"
title={row.original.conta}
>
{row.original.conta}
</div>
<div
className={`w-[130px] text-right font-semibold whitespace-nowrap ${
row.original.valor < 0
? "text-red-600"
: "text-gray-900"
}`}
>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor)}
</div>
<div className="w-[120px] text-right whitespace-nowrap">
{row.original.valor_previsto && row.original.valor_previsto !== 0 ? (
<span className={`font-semibold ${
row.original.valor_previsto < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_previsto)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[130px] text-right whitespace-nowrap">
{row.original.valor_confirmado && row.original.valor_confirmado !== 0 ? (
<span className={`font-semibold ${
row.original.valor_confirmado < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_confirmado)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[140px] text-right whitespace-nowrap">
{row.original.valor_pago && row.original.valor_pago !== 0 ? (
<span className={`font-semibold ${
row.original.valor_pago < 0 ? "text-red-600" : "text-gray-900"
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(row.original.valor_pago)}
</span>
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[20px] whitespace-nowrap"></div>
<div
className="w-[320px] text-gray-700 truncate"
title={row.original.historico}
>
{row.original.historico}
</div>
<div
className="w-[500px] text-gray-700 truncate"
title={row.original.historico2}
>
{row.original.historico2}
</div>
<div className="w-[30px] text-gray-500 whitespace-nowrap">
{row.original.numero_lancamento || "-"}
{String(cell.renderValue() || '')}
</div>
))}
</div>
);
})}
@ -843,29 +788,32 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{data.length > 0 && (
<div className="bg-gradient-to-r from-gray-50 to-gray-100 border-t border-gray-200 sticky bottom-0 z-10">
<div className="flex items-center px-4 py-3 text-sm font-semibold text-gray-900">
<div className="w-[140px] whitespace-nowrap text-gray-600">
TOTAL: {table.getRowModel().rows.length} registros
</div>
<div className="w-[120px] whitespace-nowrap"></div>
<div className="w-[120px] whitespace-nowrap"></div>
<div className="w-[120px] whitespace-nowrap"></div>
<div className="w-[220px] whitespace-nowrap"></div>
<div className="w-[140px] whitespace-nowrap"></div>
<div className="w-[130px] whitespace-nowrap"></div>
<div className="w-[160px] whitespace-nowrap"></div>
{table.getHeaderGroups().map(headerGroup => (
headerGroup.headers.map((header, index) => (
<div
className={`w-[130px] text-right font-bold whitespace-nowrap ${
key={header.id}
className="whitespace-nowrap"
style={{ width: header.getSize() }}
>
{index === 0 && (
<span className="text-gray-600">
TOTAL: {table.getRowModel().rows.length} registros
</span>
)}
{index === 8 && (
<span className={`text-right font-bold ${
columnTotals.valorRealizado < 0
? "text-red-600"
: "text-green-600"
}`}
>
}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorRealizado)}
</div>
<div className="w-[120px] text-right whitespace-nowrap">
</span>
)}
{index === 9 && (
<span className="text-right">
{columnTotals.valorPrevisto !== 0 ? (
<span className={`font-bold ${
columnTotals.valorPrevisto < 0 ? "text-red-600" : "text-green-600"
@ -878,8 +826,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[130px] text-right whitespace-nowrap">
</span>
)}
{index === 10 && (
<span className="text-right">
{columnTotals.valorConfirmado !== 0 ? (
<span className={`font-bold ${
columnTotals.valorConfirmado < 0 ? "text-red-600" : "text-green-600"
@ -892,8 +842,10 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
) : (
<span className="text-gray-500">-</span>
)}
</div>
<div className="w-[140px] text-right whitespace-nowrap">
</span>
)}
{index === 11 && (
<span className="text-right">
{columnTotals.valorPago !== 0 ? (
<span className={`font-bold ${
columnTotals.valorPago < 0 ? "text-red-600" : "text-green-600"
@ -906,11 +858,11 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
) : (
<span className="text-gray-500">-</span>
)}
</span>
)}
</div>
<div className="w-[20px] whitespace-nowrap"></div>
<div className="w-[320px] whitespace-nowrap"></div>
<div className="w-[500px] whitespace-nowrap"></div>
<div className="w-[30px] whitespace-nowrap"></div>
))
))}
</div>
</div>
)}

View File

@ -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<string, number> = {};
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 && (
<div className="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
{/* Table Header */}
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200 sticky top-0 z-20">
<div className="p-2 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200 sticky top-0 z-20">
<div className="flex items-center justify-between px-4 py-1">
<div className="flex items-center gap-2 text-xs font-semibold text-gray-700 uppercase tracking-wide">
<div className="flex-1 min-w-[300px] max-w-[400px]">Descrição</div>