fix: dupla visão

This commit is contained in:
Alessandro Gonçaalves 2025-10-22 12:49:04 -03:00
parent acb094271b
commit 75e452312c
2 changed files with 191 additions and 228 deletions

View File

@ -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<string | null>(null);
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set());
const [expandedSubgrupos, setExpandedSubgrupos] = useState<Set<string>>(
new Set()
);
const [expandedCentros, setExpandedCentros] = useState<Set<string>>(
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<string, DREItem[]>);
// 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<string, DREItem[]>);
// 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<string, DREItem[]>);
// 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<string, DREItem[]>);
}
});
} 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<string, DREItem[]>);
// 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<string, DREItem[]>);
// 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}
</span>
{isCalculado && (
// <span className="text-blue-600 font-bold text-sm">⚡</span>
<></>
<span className="text-blue-600 font-bold text-sm"></span>
)}
</div>
</button>
</div>
);
case "subgrupo":
return (
<div className="flex items-center gap-2 whitespace-nowrap">
<button
onClick={() => toggleSubgrupo(`${row.grupo}-${row.subgrupo}`)}
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-150 ease-in-out flex items-center justify-center w-8 h-8 flex-shrink-0 transform hover:scale-105"
>
{row.isExpanded ? (
<ChevronDown className="w-4 h-4 text-blue-600 transition-transform duration-150" />
) : (
<ChevronRight className="w-4 h-4 text-blue-600 transition-transform duration-150" />
)}
</button>
<button
onClick={() => handleRowClick(row)}
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
>
<span className="font-semibold text-gray-800">
{row.subgrupo}
</span>
</button>
</div>
);
case "centro_custo":
return (
<div className="flex items-center gap-2 whitespace-nowrap">
{ordemHierarquiaContasPrimeiro ? (
// Nova ordem: Centro de custo é level 3 (sem botão de toggle)
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
<span className="text-gray-400 font-bold text-lg"></span>
</div>
) : (
// Ordem original: Centro de custo é level 2 (com botão de toggle)
<button
onClick={() =>
toggleCentro(`${row.grupo}-${row.subgrupo}-${row.centro_custo}`)
}
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-150 ease-in-out flex items-center justify-center w-8 h-8 flex-shrink-0 transform hover:scale-105"
>
{row.isExpanded ? (
<ChevronDown className="w-4 h-4 text-blue-600 transition-transform duration-150" />
) : (
<ChevronRight className="w-4 h-4 text-blue-600 transition-transform duration-150" />
)}
</button>
)}
<button
onClick={() => handleRowClick(row)}
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
>
<span className="font-medium text-gray-700">
{row.centro_custo}
</span>
</button>
</div>
);
case "conta":
return (
<div className="flex items-center gap-2 whitespace-nowrap">
{ordemHierarquiaContasPrimeiro ? (
// Nova ordem: Conta é level 2 (com botão de toggle)
<button
onClick={() =>
toggleCentro(`${row.grupo}-${row.subgrupo}-${row.conta}`)
}
// Nova ordem: Conta é level 1 (com botão de toggle)
<button
onClick={() => toggleCentro(`${row.grupo}-${row.conta}`)}
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-150 ease-in-out flex items-center justify-center w-8 h-8 flex-shrink-0 transform hover:scale-105"
>
{row.isExpanded ? (
@ -1111,9 +1029,9 @@ export default function Teste() {
) : (
<ChevronRight className="w-4 h-4 text-blue-600 transition-transform duration-150" />
)}
</button>
</button>
) : (
// Ordem original: Conta é level 3 (sem botão de toggle)
// Ordem original: Conta é level 2 (sem botão de toggle)
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
<span className="text-gray-400 font-bold text-lg"></span>
</div>
@ -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"
>
<span className="font-normal text-gray-600">{row.conta}</span>
<div className="flex items-center gap-2">
<span className="font-semibold text-gray-800">
{row.conta}
</span>
{row.codigo_conta && (
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
{row.codigo_conta}
</span>
)}
</div>
</button>
</div>
);
case "centro_custo":
return (
<div className="flex items-center gap-2 whitespace-nowrap">
{ordemHierarquiaContasPrimeiro ? (
// Nova ordem: Centro de custo é level 2 (sem botão de toggle)
<div className="w-8 h-8 flex items-center justify-center flex-shrink-0">
<span className="text-gray-400 font-bold text-lg"></span>
</div>
) : (
// Ordem original: Centro de custo é level 1 (com botão de toggle)
<button
onClick={() => toggleCentro(`${row.grupo}-${row.centro_custo}`)}
className="p-2 hover:bg-blue-100 rounded-lg transition-all duration-150 ease-in-out flex items-center justify-center w-8 h-8 flex-shrink-0 transform hover:scale-105"
>
{row.isExpanded ? (
<ChevronDown className="w-4 h-4 text-blue-600 transition-transform duration-150" />
) : (
<ChevronRight className="w-4 h-4 text-blue-600 transition-transform duration-150" />
)}
</button>
)}
<button
onClick={() => handleRowClick(row)}
className="flex-1 text-left hover:bg-blue-50/50 p-2 rounded-lg cursor-pointer transition-all duration-200 truncate"
>
<div className="flex items-center gap-2">
<span className="font-medium text-gray-700">
{row.codigo_centro_custo
? `${row.centro_custo} - ${row.codigo_centro_custo}`
: row.centro_custo
}
</span>
</div>
</button>
</div>
);
@ -1527,7 +1490,6 @@ export default function Teste() {
row={row}
index={index}
toggleGroup={toggleGroup}
toggleSubgrupo={toggleSubgrupo}
toggleCentro={toggleCentro}
handleRowClick={handleRowClick}
getRowStyle={getRowStyle}

View File

@ -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