Merge pull request #36 from JurunenseDevInterno/dev

Dev
This commit is contained in:
Alessandro Gonçalves 2025-11-07 19:23:36 -03:00 committed by GitHub
commit f1cfe13ab4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 258 additions and 160 deletions

View File

@ -51,6 +51,7 @@ interface AnaliticoItem {
codigo_conta: string;
conta: string;
codigo_centrocusto: string;
centro_custo?: string;
valor: number;
historico: string;
historico2: string;
@ -65,6 +66,10 @@ interface AnaliticoItem {
numero_lancamento?: number;
ano_mes_comp?: string;
codgrupo?: string;
// Novos campos
data_lancamento?: string;
data_compensacao?: string;
data_pagto?: string;
}
interface AnaliticoProps {
@ -397,9 +402,14 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
console.log('✅ Resposta da API recebida:', result.length, 'registros');
console.log('📝 Primeiros 2 registros:', result.slice(0, 2));
console.log('🔍 Verificando campos específicos:', {
data_lancamento: result[0]?.data_lancamento,
data_compensacao: result[0]?.data_compensacao,
data_vencimento: result[0]?.data_vencimento,
data_caixa: result[0]?.data_caixa,
data_pagto: result[0]?.data_pagto,
entidade: result[0]?.entidade,
tipo_parceiro: result[0]?.tipo_parceiro,
centro_custo: result[0]?.centro_custo,
valor: result[0]?.valor,
tipo_valor: typeof result[0]?.valor
});
@ -457,38 +467,74 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
);
}, [data, filteredData, columnFilters, columnSorts, handleColumnFilterChange, handleColumnSortChange]);
// Definir colunas do DataGridPro
// Definir colunas do DataGridPro na ordem solicitada
const columns = React.useMemo(() => {
const baseColumns = [
{
field: "data_vencimento",
headerName: "Dt Venc",
width: 150,
sortable: true,
resizable: true,
renderCell: (params: any) => {
const dateCellRenderer = (params: any) => {
if (!params.value) return "-";
try {
return new Date(params.value).toLocaleDateString("pt-BR");
} catch (error) {
return params.value;
}
};
const currencyCellRenderer = (params: any, showZero: boolean = false) => {
const value = params.value;
if (value === null || value === undefined || value === "") return "-";
if (!showZero && value === 0) return "-";
const numValue = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(numValue)) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(numValue);
return (
<span className={`text-sm font-semibold ${numValue < 0 ? "text-red-600" : "text-gray-900"}`}>
{formatted}
</span>
);
};
const baseColumns = [
{
field: "data_lancamento",
headerName: "Dt Lanc",
width: 120,
sortable: true,
resizable: true,
renderCell: dateCellRenderer,
},
{
field: "data_compensacao",
headerName: "Dt Comp",
width: 120,
sortable: true,
resizable: true,
renderCell: dateCellRenderer,
},
{
field: "data_vencimento",
headerName: "Dt Venc",
width: 120,
sortable: true,
resizable: true,
renderCell: dateCellRenderer,
},
{
field: "data_caixa",
headerName: "Dt Caixa",
width: 130,
width: 120,
sortable: true,
resizable: true,
renderCell: (params: any) => {
if (!params.value) return "-";
try {
return new Date(params.value).toLocaleDateString("pt-BR");
} catch (error) {
return params.value;
}
renderCell: dateCellRenderer,
},
{
field: "data_pagto",
headerName: "Dt Pagto",
width: 120,
sortable: true,
resizable: true,
renderCell: dateCellRenderer,
},
{
field: "entidade",
@ -498,10 +544,18 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
resizable: true,
renderCell: (params: any) => params.value || "-",
},
{
field: "tipo_parceiro",
headerName: "Tipo Parc",
width: 120,
sortable: true,
resizable: true,
renderCell: (params: any) => params.value || "-",
},
{
field: "codigo_fornecedor",
headerName: "Cod.Fornec",
width: 140,
width: 120,
sortable: true,
resizable: true,
},
@ -515,15 +569,24 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
},
{
field: "codigo_centrocusto",
headerName: "C Custo",
width: 130,
headerName: "Cod.CC",
width: 100,
sortable: true,
resizable: true,
},
{
field: "centro_custo",
headerName: "Centro Custo",
flex: 1,
minWidth: 200,
sortable: true,
resizable: true,
renderCell: (params: any) => params.value || "-",
},
{
field: "codigo_conta",
headerName: "Cod.Conta",
width: 150,
width: 120,
sortable: true,
resizable: true,
},
@ -542,21 +605,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 140,
sortable: true,
resizable: true,
renderCell: (params: any) => {
const value = params.value;
if (value === null || value === undefined || value === "") return "-";
const numValue = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(numValue)) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(numValue);
return (
<span className={`text-sm font-semibold ${numValue < 0 ? "text-red-600" : "text-gray-900"}`}>
{formatted}
</span>
);
},
renderCell: (params: any) => currencyCellRenderer(params, true),
},
{
field: "valor_previsto",
@ -565,21 +614,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 130,
sortable: true,
resizable: true,
renderCell: (params: any) => {
const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-";
const numValue = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(numValue)) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(numValue);
return (
<span className={`text-sm font-semibold ${numValue < 0 ? "text-red-600" : "text-gray-900"}`}>
{formatted}
</span>
);
},
renderCell: (params: any) => currencyCellRenderer(params, false),
},
{
field: "valor_confirmado",
@ -588,44 +623,16 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 140,
sortable: true,
resizable: true,
renderCell: (params: any) => {
const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-";
const numValue = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(numValue)) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(numValue);
return (
<span className={`text-sm font-semibold ${numValue < 0 ? "text-red-600" : "text-gray-900"}`}>
{formatted}
</span>
);
},
renderCell: (params: any) => currencyCellRenderer(params, false),
},
{
field: "valor_pago",
headerName: "Vl.Pago",
type: "number" as const,
width: 130,
width: 120,
sortable: true,
resizable: true,
renderCell: (params: any) => {
const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-";
const numValue = typeof value === "string" ? parseFloat(value) : Number(value);
if (isNaN(numValue)) return "-";
const formatted = new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(numValue);
return (
<span className={`text-sm font-semibold ${numValue < 0 ? "text-red-600" : "text-gray-900"}`}>
{formatted}
</span>
);
},
renderCell: (params: any) => currencyCellRenderer(params, false),
},
{
field: "historico",
@ -646,7 +653,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
{
field: "numero_lancamento",
headerName: "Num.Lanc",
width: 80,
width: 100,
sortable: true,
resizable: true,
renderCell: (params: any) => params.value || "-",
@ -740,25 +747,36 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
if (sortedAndFilteredData.length === 0) return;
const exportData = sortedAndFilteredData.map((item) => ({
"Data Vencimento": item.data_vencimento
"DTLANC": item.data_lancamento
? new Date(item.data_lancamento).toLocaleDateString("pt-BR")
: "-",
"DTCOMPENSACAO": item.data_compensacao
? new Date(item.data_compensacao).toLocaleDateString("pt-BR")
: "-",
"DTVENC": item.data_vencimento
? new Date(item.data_vencimento).toLocaleDateString("pt-BR")
: "-",
"Data Caixa": item.data_caixa
"DTCAIXA": item.data_caixa
? new Date(item.data_caixa).toLocaleDateString("pt-BR")
: "-",
"Entidade": item.entidade || "-",
"Código Fornecedor": item.codigo_fornecedor || "-",
"Fornecedor": item.nome_fornecedor || "-",
"Código Centro Custo": item.codigo_centrocusto || "-",
"Código Conta": item.codigo_conta || "-",
"Conta": item.conta || "-",
"Valor Realizado": typeof item.valor === "string" ? parseFloat(item.valor) : (item.valor || 0),
"Valor Previsto": typeof item.valor_previsto === "string" ? parseFloat(item.valor_previsto) : (item.valor_previsto || 0),
"Valor Confirmado": typeof item.valor_confirmado === "string" ? parseFloat(item.valor_confirmado) : (item.valor_confirmado || 0),
"Valor Pago": typeof item.valor_pago === "string" ? parseFloat(item.valor_pago) : (item.valor_pago || 0),
"Histórico": item.historico || "-",
"Histórico 2": item.historico2 || "-",
"Número Lançamento": item.numero_lancamento || "-",
"DTPAGTO": item.data_pagto
? new Date(item.data_pagto).toLocaleDateString("pt-BR")
: "-",
"ENTIDADE": item.entidade || "-",
"TIPOPARCEIRO": item.tipo_parceiro || "-",
"CODFORNEC": item.codigo_fornecedor || "-",
"FORNECEDOR": item.nome_fornecedor || "-",
"CODIGOCENTROCUSTO": item.codigo_centrocusto || "-",
"CENTROCUSTO": item.centro_custo || "-",
"CODCONTA": item.codigo_conta || "-",
"CONTA": item.conta || "-",
"VLREALIZADO": typeof item.valor === "string" ? parseFloat(item.valor) : (item.valor || 0),
"VLPREVISTO": typeof item.valor_previsto === "string" ? parseFloat(item.valor_previsto) : (item.valor_previsto || 0),
"VLCONFIRMADO": typeof item.valor_confirmado === "string" ? parseFloat(item.valor_confirmado) : (item.valor_confirmado || 0),
"VLPAGO": typeof item.valor_pago === "string" ? parseFloat(item.valor_pago) : (item.valor_pago || 0),
"HISTORICO": item.historico || "-",
"HISTORICO2": item.historico2 || "-",
"NUMLANC": item.numero_lancamento || "-",
}));
const wb = XLSX.utils.book_new();
@ -869,8 +887,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
)}
</Button>
)}
{data.length > 0 && (
<div className="flex items-center gap-2">
{data.length > 0 && (
<Button
onClick={clearAllFilters}
variant="outline"
@ -885,17 +903,18 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
</span>
)}
</Button>
)}
<Button
onClick={exportToExcel}
variant="outline"
size="sm"
className="flex items-center gap-2 bg-white border-gray-300 hover:bg-green-50 hover:border-green-300 text-gray-700"
disabled={sortedAndFilteredData.length === 0}
className="flex items-center gap-2 bg-white border-gray-300 hover:bg-green-50 hover:border-green-300 text-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Download className="h-4 w-4" />
Exportar XLSX
</Button>
</div>
)}
</div>
</div>
@ -920,7 +939,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
</div>
</div>
<div style={{ height: "calc(100% - 2rem)", width: "100%", position: "relative" }}>
<div style={{ height: "calc(100% - 2rem)", width: "100%", position: "relative", display: "flex", flexDirection: "column" }}>
<DataGridPremium
key={`datagrid-${sortedAndFilteredData.length}-${Object.keys(columnFilters).length}`}
rows={sortedAndFilteredData}
@ -936,27 +955,99 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
getRowId={(row: any) => row.id || `row-${row.recnum || Math.random()}`}
sx={{
overflowAnchor: 'none',
"& .MuiDataGrid-columnHeaders": {
position: "sticky",
top: 0,
height: "100%",
display: "flex",
flexDirection: "column",
"& .MuiDataGrid-root": {
display: "flex",
flexDirection: "column",
height: "100%",
flex: 1,
overflow: "hidden",
},
"& .MuiDataGrid-main": {
overflow: "hidden !important",
position: "relative",
height: "100%",
display: "flex",
flexDirection: "column",
flex: 1,
},
// Container dos headers - DEVE FICAR FIXO (não rola)
"& .MuiDataGrid-container--top": {
overflow: "hidden !important",
position: "relative",
zIndex: 100,
backgroundColor: "#f9fafb",
zIndex: 2,
flexShrink: 0,
flexGrow: 0,
},
"& .MuiDataGrid-columnHeaders": {
position: "relative !important",
backgroundColor: "#f9fafb !important",
zIndex: 100,
borderBottom: "1px solid #e5e7eb",
},
"& .MuiDataGrid-columnHeader": {
backgroundColor: "#f9fafb !important",
},
"& .MuiDataGrid-columnHeaderRow": {
backgroundColor: "#f9fafb !important",
},
"& .MuiDataGrid-columnHeadersInner": {
backgroundColor: "#f9fafb !important",
},
"& .MuiDataGrid-cell": {
borderBottom: "1px solid #f0f0f0",
fontSize: "0.875rem",
},
// Container do virtualScroller - deve ter scroll e ocupar espaço restante
"& .MuiDataGrid-container--bottom": {
flex: 1,
overflow: "hidden !important",
position: "relative",
minHeight: 0,
display: "flex",
flexDirection: "column",
},
// Apenas o virtualScroller deve ter scroll
"& .MuiDataGrid-virtualScroller": {
overflowY: "auto",
maxHeight: "calc(40vh - 120px)",
overflowAnchor: 'none'
overflowY: "auto !important",
overflowX: "auto !important",
height: "100% !important",
flex: 1,
overflowAnchor: 'none',
// Garantir que a barra de scroll seja visível
scrollbarWidth: "thin",
"&::-webkit-scrollbar": {
height: "8px",
width: "8px",
},
"&::-webkit-scrollbar-track": {
background: "#f1f1f1",
},
"&::-webkit-scrollbar-thumb": {
background: "#888",
borderRadius: "4px",
},
"&::-webkit-scrollbar-thumb:hover": {
background: "#555",
},
},
"& .MuiDataGrid-virtualScrollerContent": {
minWidth: "max-content",
},
"& .MuiDataGrid-row": {
minWidth: "max-content",
},
"& .MuiDataGrid-toolbarContainer": {
backgroundColor: "#f8fafc",
borderBottom: "1px solid #e5e7eb",
padding: "8px 16px",
},
"& .MuiDataGrid-scrollbar": {
display: "none",
},
// Ocultar todos os ícones nativos das colunas
"& .MuiDataGrid-columnHeaderMenuContainer": {
display: "none !important",

View File

@ -2371,7 +2371,7 @@ export default function Teste() {
Filtros
</Button>
</SheetTrigger>
<SheetContent className="w-[400px] sm:w-[540px]">
<SheetContent className="w-[400px] sm:w-[540px] flex flex-col">
<SheetHeader>
<SheetTitle>Filtros</SheetTitle>
<SheetDescription>
@ -2379,7 +2379,8 @@ export default function Teste() {
</SheetDescription>
</SheetHeader>
<div className="grid flex-1 auto-rows-min gap-3 py-2">
<div className="flex-1 overflow-y-auto pr-2">
<div className="grid gap-3 py-2">
{/* Período */}
<div className="grid gap-2">
<Label>Período</Label>
@ -2698,8 +2699,9 @@ export default function Teste() {
</div>
</div>
</div>
</div>
<SheetFooter className="flex gap-2">
<SheetFooter className="flex gap-2 mt-4 border-t pt-4">
<Button variant="outline" onClick={limparFiltros} className="flex-1">
Limpar Filtros
</Button>
@ -2779,31 +2781,31 @@ export default function Teste() {
{/* Table Container */}
{filtrosAplicados && !loading && !error && (
<div className="bg-white rounded-xl shadow-lg border border-gray-200">
{/* Scroll Container */}
<div className="overflow-x-auto overflow-y-auto max-h-[500px]">
<div className="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
{/* Scroll Container - Apenas um container com scroll */}
<div className="overflow-auto max-h-[500px]" style={{ scrollbarWidth: 'thin' }}>
{/* Table */}
<table className="w-full border-collapse">
{/* Table Header */}
<thead>
<tr className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200">
<th className="px-4 py-2 text-left text-xs font-semibold text-gray-700 uppercase tracking-wide w-[300px] min-w-[300px]">
<thead className="sticky top-0 z-10 bg-gradient-to-r from-blue-50 to-indigo-50">
<tr className="border-b border-gray-200">
<th className="px-4 py-2 text-left text-xs font-semibold text-gray-700 uppercase tracking-wide w-[300px] min-w-[300px] bg-gradient-to-r from-blue-50 to-indigo-50">
Descrição
</th>
{mesesDisponiveis.map((mes) => (
<React.Fragment key={mes}>
<th className="px-2 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px]">
<th className="px-2 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px] bg-gradient-to-r from-blue-50 to-indigo-50">
{mes}
</th>
<th className="px-2 py-2 text-center text-xs font-semibold text-gray-500 uppercase tracking-wide w-[100px] min-w-[100px]">
<th className="px-2 py-2 text-center text-xs font-semibold text-gray-500 uppercase tracking-wide w-[100px] min-w-[100px] bg-gradient-to-r from-blue-50 to-indigo-50">
%
</th>
</React.Fragment>
))}
<th className="px-4 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px]">
<th className="px-4 py-2 text-right text-xs font-semibold text-gray-700 uppercase tracking-wide w-[120px] min-w-[120px] bg-gradient-to-r from-blue-50 to-indigo-50">
Total
</th>
<th className="px-2 py-2 text-center text-xs font-semibold text-gray-500 uppercase tracking-wide w-[100px] min-w-[100px]">
<th className="px-2 py-2 text-center text-xs font-semibold text-gray-500 uppercase tracking-wide w-[100px] min-w-[100px] bg-gradient-to-r from-blue-50 to-indigo-50">
%
</th>
</tr>

View File

@ -139,11 +139,12 @@ export async function GET(request: NextRequest) {
recnum: item.NUMLANC || 0,
data_competencia: item.ANOMESCOMP || "",
data_vencimento: item.DTVENC ? new Date(item.DTVENC).toISOString().split('T')[0] : "",
data_pagamento: "", // Não existe na tabela Oracle
data_pagamento: item.DTPAGTO ? new Date(item.DTPAGTO).toISOString().split('T')[0] : "",
data_caixa: item.DTCAIXA ? new Date(item.DTCAIXA).toISOString().split('T')[0] : "",
codigo_conta: item.CODCONTA || "",
conta: item.CONTA || "",
codigo_centrocusto: item.CODIGOCENTROCUSTO || "",
centro_custo: item.CENTROCUSTO || "",
valor: item.VLREALIZADO !== null && item.VLREALIZADO !== undefined ? Number(item.VLREALIZADO) : 0,
historico: item.HISTORICO || "",
historico2: item.HISTORICO2 || "",
@ -157,7 +158,11 @@ export async function GET(request: NextRequest) {
valor_pago: item.VLPAGO !== null && item.VLPAGO !== undefined ? Number(item.VLPAGO) : 0,
numero_lancamento: item.NUMLANC || 0,
ano_mes_comp: item.ANOMESCOMP || "",
codgrupo: item.CODGRUPO || ""
codgrupo: item.CODGRUPO || "",
// Novos campos
data_lancamento: item.DTLANC ? new Date(item.DTLANC).toISOString().split('T')[0] : "",
data_compensacao: item.DTCOMPENSACAO ? new Date(item.DTCOMPENSACAO).toISOString().split('T')[0] : "",
data_pagto: item.DTPAGTO ? new Date(item.DTPAGTO).toISOString().split('T')[0] : ""
};
});